スペースと特殊文字を含む cp はターミナルでは正常に動作しますが、スクリプトでは動作しません。

スペースと特殊文字を含む cp はターミナルでは正常に動作しますが、スクリプトでは動作しません。

ファイルを検索して特定のフォルダーにコピーするスクリプトを作成しようとしています。ファイル名にはスペースやその他の特殊文字が含まれています。

a=$(ls ~/Downloads/ | grep $1)
a="'$a'"
a="~/Downloads/"$a
echo $a
cp $a ~/Documents/books/.

ファイル名を引用符で囲むと、ターミナルでは問題なく動作します。しかし、スクリプトでは、単語は別々の引数として扱われます。上記のスクリプトで $a がエコーアウトされると、次のような結果が出力されます。

~/Downloads/'Ian Goodfellow, Yoshua Bengio, Aaron Courville - Deep Learning [pre-pub version] (2016, MIT Press).pdf'

ファイル名が引用符で囲まれていることがわかります。上記の引数がターミナル上の cp に渡されると正常に動作しますが、スクリプトでは動作しません。助けてください。

答え1

特定のファイルを から にコピーしたいよう~/Downloadsです~/Documents/books

まず注意すべきことは、 は~変数として動作しないということです。特に、ない引用符で囲むと、ホーム ディレクトリのパスに展開されます。したがって、スクリプトでは、$HOMEホーム ディレクトリを参照するために を使用する方が適切です。

2 つ目は、変数の値にリテラルな一重引用符を追加しているように見えることです$a。これは必要ではなく、「そのようなファイルまたはディレクトリはありません」というエラーが発生するだけです (ファイル名に一重引用符が含まれていないため)。

3 つ目は、スクリプト内で の使用を避けるようにすることですls。この場合、たとえば$1が単にファイル名である場合は~/Downloads、検索する必要はまったくありません。また、 の使用はgrep $1さまざまな理由で問題があります。

  • grep主にテキストで使用される文書ファイル名には理論的には改行文字が含まれる場合があり、ファイル名の「grep」は、名前が「nice」であることがわかっている場合にのみ機能します。
  • $1grepは引用符で囲まれていないため、スペースやその他の特殊文字が含まれていると混乱する可能性があります。$1が の場合*、たとえば現在のディレクトリ内のすべてのファイル名に展開されます。
  • 文字列が$1ダッシュで始まる場合、 のオプションとして扱われますgrep

したがって、スクリプトは次のように記述できます。

#!/bin/sh

name="$HOME/Downloads/$1"

printf 'The filename is "%s"\n' "$name"

cp "$name" "$HOME/Documents/books"

ここでのコマンドprintfは、選択したファイルの名前を出力するものです。出力名前を二重引用符で囲みますが、名前はない$name二重引用符で囲むのではなく、最後の行の呼び出しを引用符で囲むようにしています cp。実際、全て私が知っている展開では、そうしないとシェルが変数の値を分割することになります。

このスクリプトは次のように使用します

./script.sh 'Ian Goodfellow, Yoshua Bengio, Aaron Courville - Deep Learning [pre-pub version] (2016, MIT Press).pdf'

ファイル名にはスペースやシェル内で特別な意味を持つ文字が含まれているため、引用符で囲む必要があります。

スクリプトでコマンドラインで指定した単語を含むファイルを検索したい場合は、次のようにします。

./script.sh Goodfellow

次に、指定された文字列をグロブ パターンで使用します。

#!/bin/sh

for name in "$HOME/Downloads"/*"$1"*.pdf; do
    printf 'Will copy the file "%s"\n' "$name"
    cp "$name" "$HOME/Documents/books"
done

ここで、スクリプトは最初の引数を受け取り、ファイル名に指定された文字列を含む.pdf内のすべてのファイルをループします~/Downloads。このような各ファイルは にコピーされます~/Documents/books

ファイルを詳細にコピーするUnix を使用している場合はcp -v、スクリプトをさらに縮小できます。

#!/bin/sh

cp -v "$HOME/Downloads"/*"$1"*.pdf "$HOME/Documents/books"

これにより、指定されたパターンに一致するファイル名をループしなくなるため、出力を自分でフォーマットする必要がなくなります ("コピーします...")。代わりに、複数のファイル名を指定した場合cp(グロブ パターンが複数のファイル名に拡張される可能性があります)、これらを 1 つのコマンドで宛先ディレクトリにコピーできるという事実に依存します。


参照:

関連情報