Jamais, jamais utiliserfor foo in $(cat bar)
. Il s'agit d'une erreur classique, communément appelée bash pitfall number 1 . Vous devriez plutôt utiliser:
while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt
Lorsque vous exécutez la for
boucle, bash appliquera wordsplitting à ce qu'il lit, ce qui signifie que a strange blue cloud
sera lu comme a
, strange
, blue
et cloud
:
$ cat files
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt
Comparer aux:
$ while IFS= read -r file; do echo "$file"; done < files
a strange blue cloud.txt
Ou même, si vous insistez sur l' UUoC :
$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt
Ainsi, la while
boucle lira son entrée et utilisera le read
pour affecter chaque ligne à une variable. Le IFS=
définit le séparateur de champ d'entrée sur NULL * , et l' -r
option de l' read
empêche d'interpréter les échappements de barre oblique inverse (de sorte que cela \t
soit traité comme une barre oblique + t
et non comme un onglet). L' --
après le mv
signifie "tout traiter après le - comme un argument et non une option", ce qui vous permet de traiter -
correctement les noms de fichiers commençant par .
* Cela n'est pas nécessaire ici, à proprement parler, le seul avantage de ce scénario est qu'il empêche read
de supprimer les espaces blancs de début ou de fin, mais c'est une bonne habitude à prendre lorsque vous devez traiter des noms de fichiers contenant des caractères de nouvelle ligne, ou en général, lorsque vous devez être capable de gérer des noms de fichiers arbitraires.
IFS=
n'aidera pas avec les nouvelles lignes dans les noms de fichiers. Le format du fichier ici ne permet tout simplement pas les noms de fichiers avec des retours à la ligne.IFS=
est nécessaire pour les noms de fichiers commençant par un espace ou une tabulation (ou par tout autre caractère que la nouvelle ligne $ IFS contenue auparavant).ls
oufind
avec des substitutions de commandes lorsqu'il existe des moyens meilleurs et plus fiables de le faire.$(cat file)
serait bien quand bien fait. Il est à peu près aussi difficile de "bien faire les choses" avec une boucle de lecture while qu'avec une boucle for + $ (...). Voir ma réponse.Dans
$(cat file_list.txt)
les shells POSIX commebash
dans le contexte de la liste, l'opérateur split + glob (zsh
ne fait que la partie split comme vous vous y attendez).Il se divise en caractères de
$IFS
(par défaut, SPC , TAB et NL) et ne fait de glob que si vous désactivez complètement la globalisation.Ici, vous voulez diviser uniquement sur la nouvelle ligne et ne voulez pas la partie glob, donc ça devrait être:
Cela a également l'avantage (sur une
while read
boucle) de supprimer les lignes vides, de conserver une ligne non terminée et de conservermv
le stdin (nécessaire en cas d'invites par exemple).Il présente cependant l'inconvénient que le contenu complet du fichier doit être stocké en mémoire (plusieurs fois avec des coques comme
bash
etzsh
).Avec certains shells (
ksh
,zsh
et dans une moindre mesurebash
), vous pouvez l'optimiser avec$(<file_list.txt)
au lieu de$(cat file_list.txt)
.Pour faire l'équivalent avec une
while read
boucle, il vous faudrait:Ou avec
bash
:Ou avec
zsh
:Ou avec GNU
mv
etzsh
:Ou avec GNU
mv
et GNUxargs
et ksh / zsh / bash:Plus d'informations sur ce que signifie laisser des extensions sans guillemets sur les implications de la sécurité d'oublier de citer une variable dans des shells bash / POSIX
la source
$(cat file_list.txt)
le fichier entier est lu en mémoire avant d'itérer, alors qu'ilwhile read ...
ne lit qu'une ligne à la fois. Cela pourrait être un problème pour les fichiers volumineux.Au lieu d'écrire un script, vous pouvez utiliser find ..
pour le cas où les fichiers se trouvent dans le fichier, vous pouvez faire ce qui suit:
le second pas sûr cependant ..
Bonne chance
la source
find
, vous pouvez tout aussi bien utiliserfind
pour tout. Pourquoi y entrerxargs
?find -type f -iname '*.txt' -exec mv {} /folder/to/destination/
.-exec
se comporte parfaitement avec des noms de fichiers arbitraires (y compris ceux qui contiennent des espaces, des nouvelles lignes ou toute autre bizarrerie). Je sais comment çaxargs
marche, merci :) Je ne vois tout simplement pas l'intérêt d'appeler une commande distincte quandfind
je peux déjà le faire pour vous. Rien de mal à cela, juste inefficace.xargs
a également l'inconvénient de clobber stdin (qui amv
besoin de ses invites). Également un problème avec la solution de @ terdon. Avec GNUxargs
et coques avec substitution de processus, peut être allégé avecxargs -0IF -a <(find...) mv F /dest
#! / bin / bash
FICHIER =
zenity --title "Pick a Movie" --file-selection
pour FILE dans "$ {FILE [0]}"
faire omxplayer "$ {FILE [0]}" fait
! / bin / bash
FICHIER =
zenity --title "Pick a Song" --file-selection
pour FILE dans "$ {FILE [0]}"
jouer "$ {FILE [0]}" fait
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
pour DIR dans "$ {DIR [0]}"
faites le cd "$ {DIR [0]}" && find. -type f -nom ' .ogg' -o -nom ' .mp3' | sort --version-sort | en lisant DIR; jouer "$ DIR" fait fait
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
pour DIR dans "$ {DIR [0]}"
faites le cd "$ {DIR [0]}" && find. -type f -nom ' .ogg' -o -nom ' .mp3' | en lisant DIR; jouer "$ DIR" fait fait
la source