Comment aplatir un répertoire au format suivant?
Avant: ./aaa/bbb/ccc.png
Après: ./aaa-bbb-ccc.png
./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?
Comment aplatir un répertoire au format suivant?
Avant: ./aaa/bbb/ccc.png
Après: ./aaa-bbb-ccc.png
./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 :
zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'
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 le D
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:
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' 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 finale z
. La finale z
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):
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
' _ {} +
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
:
find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;
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 pour tr
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 les tr
appels ou en ajouter d'autres au besoin. Et je peux ajouter echo
avant mv
si je veux vérifier visuellement qu'il fera la bonne chose (puis répéter sans extra echo
uniquement si les choses semblent correctes:
find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;
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.
find . -mindepth 2 -type f -name '*' |
perl -l000ne 'print $_; s/\//-/g; s/^\.-/.\// and print' |
xargs -0n2 mv
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
rm -fr junk
rm -f junk*hello*
mkdir -p junk/junkier/junkiest
touch 'hello hello'
touch 'junk/hello hello'
touch 'junk/junkier/hello hello'
touch 'junk/junkier/junkiest/hello hello'
Résultant en
./hello hello
./junk-hello hello
./junk-junkier-hello hello
./junk-junkier-junkiest-hello hello
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-ils ls
vraiment 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; }
Il suffit de diriger le nom de fichier + le chemin vers la tr
commande.tr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cp
ligne pour cp -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 la find
sortie avec une for
boucle est défectueuse. Les seuls moyens d'utilisation sûrs find
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:
#!/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 cp
au lieu de mv
pour des raisons évidentes, mais devrait produire la même chose.
type find
-> find is /usr/bin/find
sur ma machine. L'utilisation PATH
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 la read
commande à mauvais escient (recherchez ce site IFS 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 la PATH
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
.