PS: Veuillez également tenir compte des noms de fichiers avec des caractères impairs (par exemple des espaces)
Nathan JB
3
Vous devrez également spécifier comment vous souhaitez gérer le cas possible où ./foo/bar/baz.pnget les ./foo-bar-baz.pngdeux existent. Je suppose que vous ne voulez pas remplacer ce dernier par l'ancien?
jw013
Dans mon cas, ce n'est pas pertinent, mais la réponse devrait en effet en tenir compte pour que les autres puissent s'y fier! Merci!
Nathan JB
Réponses:
7
Avertissement: j'ai tapé la plupart de ces commandes directement dans mon navigateur. Caveat lector.
Explication: Le modèle **/*correspond à tous les fichiers des sous-répertoires du répertoire actuel, de manière récursive (il ne correspond pas aux fichiers du répertoire actuel, mais ceux-ci n'ont pas besoin d'être renommés). Les deux premières paires de parenthèses sont des groupes auxquels on peut se référer en tant que $1et $2dans le texte de remplacement. La dernière paire de parenthèses ajoute le Dqualificatif glob afin que les fichiers de points ne soient pas omis. -o -isignifie passer l' -ioption à mvafin que vous soyez invité si un fichier existant serait écrasé.
Avec seulement les outils POSIX:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
Explication: l' caseinstruction omet le répertoire en cours et les sous-répertoires de niveau supérieur du répertoire en cours. targetcontient le nom du fichier source ( $0) avec le début ./supprimé et toutes les barres obliques remplacées par des tirets, plus une finale z. La finale zest là au cas où le nom de fichier se termine par une nouvelle ligne: sinon la substitution de commande le supprimerait.
Si votre findne prend pas en charge -exec … +(OpenBSD, je vous regarde):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
Avec bash (ou ksh93), vous n'avez pas besoin d'appeler une commande externe pour remplacer les barres obliques par des tirets, vous pouvez utiliser l'expansion du paramètre ksh93 avec la construction de remplacement de chaîne ${VAR//STRING/REPLACEMENT}:
find . -depth -exec bash -c '
for source; do
case $source in ./*/*)
source=${source#./}
target="${source//\//-}";
mv -i -- "$source" "$target";;
esac
done
' _ {} +
Les for sourceexemples manquent le premier fichier / répertoire présenté ... J'ai enfin trouvé pourquoi vous (normalement) l'avez utilisé _comme $0:) ... et merci pour les bonnes réponses. J'apprends tellement d'eux.
Peter.O
Notez que l'exemple zmv ne fait qu'un essai à sec: il imprime les commandes de déplacement mais ne les exécute pas. Pour exécuter réellement ces commandes, supprimez le n dans -Qn.
Peter
5
Bien qu'il existe déjà de bonnes réponses ici, je trouve cela plus intuitif, dans bash:
Où aaaest votre répertoire existant. Les fichiers qu'il contient seront déplacés vers le répertoire courant (ne laissant que des répertoires vides dans aaa). Les deux appels pour trgérer à la fois les répertoires et les espaces (sans logique pour les barres obliques échappées - un exercice pour le lecteur).
Je considère cela plus intuitif et relativement facile à modifier. C'est-à-dire que je pourrais changer les findparamètres si je dis que je veux trouver uniquement les fichiers .png. Je peux modifier les trappels ou en ajouter d'autres au besoin. Et je peux ajouter echoavant mvsi je veux vérifier visuellement qu'il fera la bonne chose (puis répéter sans extra echouniquement si les choses semblent correctes:
Notez également que j'utilise find aaa... et non find .... ou find aaa/... car les deux derniers laisseraient des artefacts amusants dans le nom de fichier final.
Merci pour cette doublure - cela a bien fonctionné pour moi quand je l'ai fait en dehors du répertoire (ce qui est exactement ce que vous avez dit de faire). Lorsque j'ai essayé de le faire depuis l'intérieur du répertoire (en espérant éviter que le nom du répertoire racine ne soit ajouté), tous les fichiers ont "disparu". Je les avais transformés en fichiers cachés dans ce même répertoire.
Remarque: cela ne fonctionnera pas pour le nom de fichier qui contient \n.
Ceci, bien sûr, ne déplace que les ffichiers de type ...
Les seuls conflits de noms proviendraient de fichiers préexistants dans le pwd
Voici une lsversion .. commentaires bienvenus. Cela fonctionne pour les noms de fichiers loufoques comme celui-ci
"j1ळ\n\001\n/j2/j3/j4/\nh h\n", mais je suis vraiment intéressé de connaître les pièges. Il s'agit plus d'un exercice dans "les calomniés peuvent-ils lsvraiment le faire (avec robustesse)?"
ls -AbQR1p * | # paths in "quoted" \escaped format
sed -n '/":$/,${/.[^/]$/p}' | # skip files in pwd, blank and dir/ lines
{ bwd="$PWD"; while read -n1; # save base dir strip leading "quote
read -r p; do # read path
printf -vs "${p%\"*}" # strip trailing quote"(:)
[[ $p == *: ]] && { # this is a directory
cd "$bwd/$s" # change into new directory
rnp="${s//\//-}"- # relative name prefix
} || mv "$s" "$bwd/$rnp$s"
done; }
Cela ne gère pas les noms de fichiers impairs en toute sécurité. Les espaces le briseront (entre autres).
jw013
À première vue, c'est une solution propre et lisible, mais @ jw013 a raison, il ne gérera pas bien les espaces. Changerait la cpligne pour cp -v "$FILE" "$NEWFILE"aider? (En outre, vous ne devez pas supposer uniquement les fichiers png.)
Nathan JB
2
@ NathanJ.Brauer L'ajout de guillemets à la cpligne est insuffisant. L'approche complète de l'analyse de la findsortie avec une forboucle est défectueuse. Les seuls moyens d'utilisation sûrs findsont avec -execou -print0s'ils sont disponibles (moins portables que -exec). Je suggérerais une alternative plus robuste mais votre question est sous-spécifiée pour le moment.
jw013
-2
Sans danger pour les espaces dans les noms de fichiers:
#!/bin/bash
/bin/find $PWD -type f | while read FILE
do
_new="${FILE//\//-}"
_new="${_new:1}"
#echo $FILE
#echo $_new
/bin/mv "$FILE" "$_new"
done
Ran mine avec cpau lieu de mvpour des raisons évidentes, mais devrait produire la même chose.
type find-> find is /usr/bin/findsur ma machine. L'utilisation PATHcomme nom de variable dans un script est une très mauvaise idée. En outre, votre script modifie les noms de fichiers avec des espaces ou des barres obliques inverses, car vous utilisez la readcommande à mauvais escient (recherchez ce site IFS read -r).
Gilles 'SO- arrête d'être méchant'
find is hashed (/bin/find), pertinent comment? Différentes distributions sont différentes?
Tim
Je savais que j'aurais dû rester loin de ça, appât vautour.
Tim
@Tim Vous pouvez toujours supprimer la réponse pour récupérer votre représentant (et le mien aussi, car cela coûte au représentant de voter). Les chemins codés en dur dans les scripts semblent suggérer que vous manquez le point de la PATHvariable d'environnement, qui est quelque chose que tout système Unix utilise. Si vous écrivez, /bin/findvotre script est en fait cassé sur tous les systèmes (Gilles, le mien et probablement les OP) qui n'en ont pas /bin/find(par exemple, b / c il est dedans /usr/bin/find). L'intérêt de la PATHvariable d'environnement est d'en faire un non-problème.
jw013
Soit dit en passant, $(pwd)est déjà disponible dans une variable: $PWD. Mais vous devez ne pas utiliser un chemin absolu ici: si votre script est appelé à partir, par exemple, /home/timil tente de renommer /home/tim/some/pathà /home-tim-some-path.
./foo/bar/baz.png
et les./foo-bar-baz.png
deux existent. Je suppose que vous ne voulez pas remplacer ce dernier par l'ancien?Réponses:
Avertissement: j'ai tapé la plupart de ces commandes directement dans mon navigateur. Caveat lector.
Avec zsh et zmv :
Explication: Le modèle
**/*
correspond à tous les fichiers des sous-répertoires du répertoire actuel, de manière récursive (il ne correspond pas aux fichiers du répertoire actuel, mais ceux-ci n'ont pas besoin d'être renommés). Les deux premières paires de parenthèses sont des groupes auxquels on peut se référer en tant que$1
et$2
dans le texte de remplacement. La dernière paire de parenthèses ajoute leD
qualificatif glob afin que les fichiers de points ne soient pas omis.-o -i
signifie passer l'-i
option àmv
afin que vous soyez invité si un fichier existant serait écrasé.Avec seulement les outils POSIX:
Explication: l'
case
instruction omet le répertoire en cours et les sous-répertoires de niveau supérieur du répertoire en cours.target
contient le nom du fichier source ($0
) avec le début./
supprimé et toutes les barres obliques remplacées par des tirets, plus une finalez
. La finalez
est là au cas où le nom de fichier se termine par une nouvelle ligne: sinon la substitution de commande le supprimerait.Si votre
find
ne prend pas en charge-exec … +
(OpenBSD, je vous regarde):Avec bash (ou ksh93), vous n'avez pas besoin d'appeler une commande externe pour remplacer les barres obliques par des tirets, vous pouvez utiliser l'expansion du paramètre ksh93 avec la construction de remplacement de chaîne
${VAR//STRING/REPLACEMENT}
:la source
for source
exemples manquent le premier fichier / répertoire présenté ... J'ai enfin trouvé pourquoi vous (normalement) l'avez utilisé_
comme$0
:) ... et merci pour les bonnes réponses. J'apprends tellement d'eux.Bien qu'il existe déjà de bonnes réponses ici, je trouve cela plus intuitif, dans
bash
:Où
aaa
est votre répertoire existant. Les fichiers qu'il contient seront déplacés vers le répertoire courant (ne laissant que des répertoires vides dans aaa). Les deux appels pourtr
gérer à la fois les répertoires et les espaces (sans logique pour les barres obliques échappées - un exercice pour le lecteur).Je considère cela plus intuitif et relativement facile à modifier. C'est-à-dire que je pourrais changer les
find
paramètres si je dis que je veux trouver uniquement les fichiers .png. Je peux modifier lestr
appels ou en ajouter d'autres au besoin. Et je peux ajouterecho
avantmv
si je veux vérifier visuellement qu'il fera la bonne chose (puis répéter sans extraecho
uniquement si les choses semblent correctes:Notez également que j'utilise
find aaa
... et nonfind .
... oufind aaa/
... car les deux derniers laisseraient des artefacts amusants dans le nom de fichier final.la source
Remarque: cela ne fonctionnera pas pour le nom de fichier qui contient
\n
.Ceci, bien sûr, ne déplace que les
f
fichiers de type ...Les seuls conflits de noms proviendraient de fichiers préexistants dans le pwd
Testé avec ce sous-ensemble de base
Résultant en
la source
Voici une
ls
version .. commentaires bienvenus. Cela fonctionne pour les noms de fichiers loufoques comme celui-ci"j1ळ\n\001\n/j2/j3/j4/\nh h\n"
, mais je suis vraiment intéressé de connaître les pièges. Il s'agit plus d'un exercice dans "les calomniés peuvent-ilsls
vraiment le faire (avec robustesse)?"la source
Il suffit de diriger le nom de fichier + le chemin vers la
tr
commande.tr <replace> <with>
la source
cp
ligne pourcp -v "$FILE" "$NEWFILE"
aider? (En outre, vous ne devez pas supposer uniquement les fichiers png.)cp
ligne est insuffisant. L'approche complète de l'analyse de lafind
sortie avec unefor
boucle est défectueuse. Les seuls moyens d'utilisation sûrsfind
sont avec-exec
ou-print0
s'ils sont disponibles (moins portables que-exec
). Je suggérerais une alternative plus robuste mais votre question est sous-spécifiée pour le moment.Sans danger pour les espaces dans les noms de fichiers:
Ran mine avec
cp
au lieu demv
pour des raisons évidentes, mais devrait produire la même chose.la source
type find
->find is /usr/bin/find
sur ma machine. L'utilisationPATH
comme nom de variable dans un script est une très mauvaise idée. En outre, votre script modifie les noms de fichiers avec des espaces ou des barres obliques inverses, car vous utilisez laread
commande à mauvais escient (recherchez ce siteIFS read -r
).find is hashed (/bin/find)
, pertinent comment? Différentes distributions sont différentes?PATH
variable d'environnement, qui est quelque chose que tout système Unix utilise. Si vous écrivez,/bin/find
votre script est en fait cassé sur tous les systèmes (Gilles, le mien et probablement les OP) qui n'en ont pas/bin/find
(par exemple, b / c il est dedans/usr/bin/find
). L'intérêt de laPATH
variable d'environnement est d'en faire un non-problème.$(pwd)
est déjà disponible dans une variable:$PWD
. Mais vous devez ne pas utiliser un chemin absolu ici: si votre script est appelé à partir, par exemple,/home/tim
il tente de renommer/home/tim/some/path
à/home-tim-some-path
.