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

なぜ最初の例は機能しますが、2番目の例は機能しませんか?どうすれば機能させることができますか?

回答:


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 2017

@ MrJingles87わかっています。これはある時点で全員を取得します。あなたがそれについて知らなければ、それは非常に直感的ではありません。
terdon

@JohnKugelmanはい、確かに、良い点です。回答を編集しました。ありがとうございます。
terdon

IFS=ファイル名の改行には役立ちません。ここでのファイルの形式は、改行を含むファイル名を許可していません。IFS=スペースまたはタブで始まるファイル名(または改行以外のすべての文字$ IFSが事前に含まれている場合)に必要です。
ステファンChazelas

そのバッチ落とし穴はよりの出力を解析しようとしている程度であるlsか、findより良いそれを行うには、より信頼性の高い方法がある場合、コマンド置換を有します。$(cat file)正しく行われていれば大丈夫でしょう。while読み取りループでfor + $(...)を実行する場合と同じように、「正しく実行する」ことはほぼ困難です。私の答えを見てください。
ステファンChazelas

11

それは$(cat file_list.txt)のようなPOSIXシェルにbashリストコンテキストで(分割+グロブ演算子でzshのみ行い、スプリットあなたが期待通りの部分が)。

$IFS(デフォルトでは、SPC、TAB、NL)の文字で分割され、グロビングを完全にオフにしない限り、グロブを実行します。

ここでは、改行のみで分割し、グロブ部分は不要なので、次のようにする必要があります。

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 を保持する(プロンプトの場合などに必要)利点があります(ループより)。

ただし、ファイルの完全なコンテンツをメモリに保存する必要があるという欠点があります(bashおよびなどのシェルでは数回zsh)。

一部のシェル(と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シェルで変数を引用するのを忘れることによるセキュリティへの影響で展開を引用しないでおくことの意味についての詳細


ただし、一度に1行しか読み取らないの$(cat file_list.txt)に対して、反復する前にファイル全体をメモリに読み取ることを認めるべきだと思いますwhile read ...。これは大きなファイルの問題になる可能性があります。
chepner 2017

@chepner。いい視点ね。追加しました。
ステファンChazelas

1

スクリプトを記述する代わりに、find ..を使用できます。

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

ファイルがfileにある場合は、次の操作を実行できます。

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は正しいです。
Noel Alex Makumuli 2017

xargsまた、stdin(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

ファイル=zenity --title "Pick a Movie" --file-selection

「$ {FILE [0]}」のFILE

omxplayer "$ {FILE [0]}"を実行しました

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

!/ bin / bash

ファイル=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]}" &&を実行します。-type f -name ' .ogg' -o -name ' .mp3' | sort --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]}" &&を実行します。-type f -name ' .ogg' -o -name ' .mp3' | DIRの読み取り中。「$ DIR」を再生します


マークダウンについて読んで、メイクアップを回答に適用してください。そして、これが質問に答える理由を説明してください。そして叫ぶな!!!

この答えはこの質問のものではないと感じています。しかし、もしそうだとすれば、そのような単純な問題のために多くのソフトウェアをインストールするのははるかに遠いことです。
MrJingles87 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.