bash:使用空格移动文件


10

当我移动文件名中带有空格的单个文件时,它的工作方式如下:

$ mv "file with spaces.txt" "new_place/file with spaces.txt"

现在,我有了一个可能包含空格的文件列表,我想移动它们。例如:

$ echo "file with spaces.txt" > file_list.txt
$ for file in $(cat file_list.txt); do mv "$file" "new_place/$file"; done;

mv: cannot stat 'file': No such file or directory
mv: cannot stat 'with': No such file or directory
mv: cannot stat 'spaces.txt': No such file or directory

为什么第一个示例有效,而第二个示例无效?我该如何运作?

Answers:


20

永远不要使用for foo in $(cat bar)。这是一个经典错误,通常称为 bash陷阱1。您应该改用:

while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt

当您运行for循环,bash将适用wordsplitting什么读取,这意味着a strange blue cloud将被读取astrangebluecloud

$ cat files 
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt

相比于:

$ while IFS= read -r file; do echo "$file"; done < files 
a strange blue cloud.txt

甚至,如果您坚持使用UUoC

$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt

因此,while循环将读取其输入,并使用read将每行分配给一个变量。在IFS=将输入字段分隔符为NULL *,和所述-r的选择read停止它从解释反斜杠(使得\t被视为斜线+ t而不是作为一个选项卡)。在--mv的意思是“对待一切之后-作为参数,而不是一个选项”,它可以让你处理开始的文件名-正确。


* 严格来说,这里不需要这样做,这种情况下的唯一好处是可以避免read删除任何前导或尾随空格,但是当您需要处理包含换行符的文件名时,这是一个好习惯通常,当您需要能够处理任意文件名时。


1
好的,我只是在寻找逃脱“ $ file”部分的方法。没想到其他地方的错误。
MrJingles87年

@ MrJingles87我知道。这使每个人都在某个时候。如果您不了解,这是非常不直观的。
terdon

@JohnKugelman是的,的确不错。答案已编辑,谢谢。
terdon

IFS=不会对文件名中的换行符有所帮助。此处的文件格式根本不允许使用带换行符的文件名。IFS=对于以空格或制表符(或除换行符$ IFS之外的其他任何字符)开头的文件名,需要使用。
斯特凡Chazelas

一批陷阱更多的是试图解析的输出lsfind与命令替换的时候有更好,更可靠的方式来做到这一点。$(cat file)只要做对就可以了。对于while读取循环,“正确地执行”与为for + $(...)一个进行难度一样大。看我的答案。
斯特凡Chazelas

11

$(cat file_list.txt)在POSIX炮弹一样bash在列表环境是割裂+水珠操作(zsh仅做了拆分正如你所期望的部分)。

它分割为 $IFS(默认情况下为SPC,TAB和NL),并且会进行glob,除非您完全关闭glob。

在这里,您只想在换行符上分割,并且不想要glob部分,因此应该是:

IFS='
' # split on newline only
set -o noglob # disable globbing

for file in $(cat file_list.txt); do # split+glob
  mv -- "$file" "new_place/$file"
done

这也具有优势(相对于 while read循环),可以丢弃空行,保留尾随的未终止行,并保留mv的stdin(例如在出现提示时需要)。

尽管确实有缺点,但文件的全部内容必须存储在内存中(多次使用诸如 bashzsh)。

对于某些壳(kshzsh以及在较小程度上bash),可以使用优化它$(<file_list.txt)代替$(cat file_list.txt)

要使用while read循环进行等效操作,您需要:

while IFS= read <&3 -r file || [ -n "$file" ]; do
  {
    [ -n "$file" ] || mv -- "$file" "new_place/$file"
  } 3<&-
done 3< file_list.txt

或搭配bash

readarray -t files < file_list.txt &&
for file in "${files[@]}"
  [ -n "$file" ] || mv -- "$file" "new_place/$file"
done

或搭配zsh

for file in ${(f)"$(<file_list.txt)"}
  mv -- "$file" "new_place/$file"
done

或与GNU mvzsh

mv -t -- new_place ${(f)"$(<file_list.txt)"}

或者使用GNU mv和GNU xargs以及ksh / zsh / bash:

xargs -rd '\n' -a <(grep . file_list.txt) mv -t -- new_place

有关在不考虑在bash / POSIX shell中引用变量的安全性隐含含义的情况下保留扩展名的含义的更多信息,请参见


我想您应该承认,尽管如此,它$(cat file_list.txt)会在迭代之前将整个文件读入内存,而一次while read ...只能读取一行。这对于大文件可能是个问题。
chepner

@chepner。好点子。添加。
斯特凡Chazelas

1

您可以使用find ..代替编写脚本。

 find -type f -iname \*.txt -print0 | xargs -IF -0 mv F /folder/to/destination/

对于文件位于文件中的情况,则可以执行以下操作:

cat file_with_spaces.txt | xargs -IF -0 mv F /folder/to/destination

第二个虽然不确定。

祝好运


3
如果您正在使用find,则最好也find用于所有内容。为什么要带xargs进去?find -type f -iname '*.txt' -exec mv {} /folder/to/destination/
terdon

xargs只是在这里操作find的结果:-I将每个结果分配给变量F,而0和print0只是保证不转义带有空格的文件。和-0防止xargs转义结果。不知道如何-exec的行为与..空间
诺埃尔·亚历Makumuli

2
-exec与任意文件名(包括包含空格,换行符或任何其他奇怪内容的文件名)完美配合使用。我知道它是如何xargs工作的,谢谢:)当find可以为您执行调用时,我只是看不到调用单独命令的意义。没错,只是效率低下。
terdon

@terdonyou是正确的。-exec {} / path / destination可以顺利运行..我更喜欢xargs,因为我可以用它做更多的事情。
Noel Alex Makumuli

xargs还具有破坏标准输入的缺点(mv需要其提示)。@terdon解决方案也是一个问题。与GNU xargs并用进程替换壳,可以与缓解xargs -0IF -a <(find...) mv F /dest
斯特凡Chazelas

-1
FIRST INSTALL 

apt-get install chromium-browser

apt-get install omxplayer

apt-get install terminator

apt-get install nano (if not already installed)

apt-get install zenity (if not already installed)

THEN CREATE A BASH SCRIPT OF ALL OF THESE PERSONALLY WRITTEN SCRIPTS. 

MAKE SURE THAT EVERY SHELL SCRIPT IS A .sh FILE ENDING EACH ONE OF THESE IS SCRIPTED TO OPEN UP A DIRECTORY FOR YOU TO FIND AND PICK WHAT YOU WANT. 

IT RUNS A BACKGROUND TERMINAL & ALLOWS YOU TO CONTROL THE SONG OR MOVIE.  

MOVIE KEYS ARE AS FOLLOWS; p or space bar for pause, q for quit, - & + for sound up and down, left arrow and right arrow for skipping forward and back. 

MUSIC PLAYER REALLY ONLY HAS A SKIP SONG AND THAT IS CTRL+C AND IF THAT IS THE LAST SONG OR ONLY SONG THEN IT SHUTS DOWN AND THE TERMINAL GOES AWAY.


****INSTRUCTIONS TO MAKE THE SCRIPTS****
- OPEN UP A TERMINAL 

- CD TO THE DIRECTORY YOU WANT THE SCRIPTS TO BE
cd /home/pi/Desktop/

- OPEN UP THE NANO EDITOR WITH THE TITLE OF SHELL YOU WANT
sudo nano Movie_Player.sh

- INSIDE NANO, TYPE OR COPY/PAST (REMEMBER THAT IN TERMINAL YOU NEED TO CTRL+SHIFT+V) THE SCRITP

- SAVE THE DATA WITH CTRL+O
ctrl+o

- HIT ENTER TO SAVE AS THAT FILE NAME OR DELETE THE FILE NAME THEN TYPE NEW ONE JUST MAKE SURE IT ENDS IN .sh THEN HIT ENTER

- NEXT YOU NEED TO SUDO CHMOD IT WITH +X TO MAKE IT CLICKABLE AS A BASH
sudo chmod +x Movie_Player.sh

- FINALLY RUN IT TO TEST EITHER BY DOUBLE CLICKING IT AND CHOOSING "EXECUTE IN TERMINAL" OR BY ./ IT IN TERMINAL
./Movie_Player.sh

YOU ARE NOW GOOD TO PICK A MOVIE OR SELECT A SONG OR ALBUM AND ENJOY!  




**** ALL OF THESE SCRIPTS ACCOUNT FOR SPACES IN THE FILENAME 
SO YOU CAN ACCESS "TOM PETTY" OR "SIXTEEN STONE" WITHOUT NEEDING THE " _ " BETWEEN THE WORDS.




-------WATCH A MOVIE SCRIPT ------- (

#!/ bin / bash

FILE =zenity --title "Pick a Movie" --file-selection

用于“ $ {FILE [0]}”中的FILE

做完omxplayer“ $ {FILE [0]}”

--------LISTEN TO A SONG -------

!/ bin / bash

FILE =zenity --title "Pick a Song" --file-selection

用于“ $ {FILE [0]}”中的FILE

确实播放“ $ {FILE [0]}”

--------LISTEN TO AN ALBUM ---------- SONGS IN ORDER

!/ bin / bash

DIR =zenity --title "Pick a Album" --file-selection --directory

“ $ {DIR [0]}”中的DIR

执行cd“ $ {DIR [0]}” &&查找。类型的f名称' .ogg'-o-名称' .mp3'| 排序--version-sort | 同时读取DIR;播放完成的“ $ DIR”

--------LISTEN TO AN ALBUM WITH SONGS IN RANDOM ORDER --------

!/ bin / bash

DIR =zenity --title "Pick a Album" --file-selection --directory

“ $ {DIR [0]}”中的DIR

执行cd“ $ {DIR [0]}” &&查找。类型的f名称' .ogg'-o-名称' .mp3'| 同时读取DIR;播放完成的“ $ DIR”


请阅读有关降价的信息,然后对您的答案进行补充。并请解释为什么这可以回答问题。而且不要喊!!!

我觉得这个答案不属于这个问题。但是,如果是这样,那么对于这么一个简单的问题,要安装这么多的软件就太困难了。
MrJingles87 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.