私は、データファイルで指定されたパラメータに基づいて静的フォルダ内のファイルを移動するLinux用のbashシェルスクリプトを作成しました。
私が経験している問題を示すために、スクリプトを再現可能な最小限の例に縮小しました。
#!/bin/bash
while IFS=" " read -r fldr matchStr
do
perlExp=\'s/^/\\/home\\/test\\/alpha\\/"$fldr"\\//\'
fullmatchStr=testfile\ -\ "$matchStr"\ -\ *.txt
rename --verbose "$perlExp" ${fullmatchStr}
done < test.dat
(私は元の代わりにmv
使用を開始しましたが、両方のコマンドのrename
いずれかを使用できます。)
スクリプトは、test.dat
次の構文で構成されるテキストファイルを読み込みます。
folderName matchStr
...
このファイルの簡単な例test.dat
:
test001 *.foo.bar
test002 *.foo.baz
test003 bar.*.baz
このデータファイルにワイルドカードが許可され、スクリプトがワイルドカードをサポートすることが重要です。また、ファイル名のスペースをサポートする必要があります。
移動するファイルのファイル名の例:
testfile - linux.foo.bar - 10101010 10101010.txt
testfile - unix.foo.bar - 01010101 10101010.txt
testfile - ubuntu.foo.baz - 10101010 01010101.txt
testfile - debian.foo.baz - 10101010 00000000.txt
testfile - bar.linus.baz - 11111111 01010101.txt
上記のtest.dat
ファイルが与えられたら、これらのファイルを個別に次のフォルダに移動する必要があります。
/home/test/alpha/test001
/home/test/alpha/test001
/home/test/alpha/test002
/home/test/alpha/test002
/home/test/alpha/test003
以下を含むいくつかの関連QAを読みました。
- mv:セルスクリプトには対応するファイルまたはディレクトリがありません。
- https://stackoverflow.com/questions/8748831/when-do-we-need-curly-braces-around-shell-variables
- スペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?
しかし、まだ希望の結果が得られませんでした。エラーは発生しませんでしたが、ファイルは移動されませんでした。
このコードが期待どおりに機能するようにどのように改善できますか?
答え1
Perlベースのコードを使用している場合rename
(そうです)、Perlコードを書くためにシェル文字列を使用しないことをお勧めします。シェルコードでできることを行うには、Perlコードを直接使用してください。
この場合、ループ内で次のことを行います。
IFS=
target="/home/test/alpha/$fldr/" rename --verbose 's/^/$ENV{target}/' "testfile - "$matchStr" - "*.txt
target
ここで置き換えるのは、コマンドに設定した環境変数の値なので、rename
Perlコードを使用して簡単に取得できます。また、IFSを空の文字列に設定すると単語の区切りが無効になり、$matchStr
ワイルドカードを安全に使用できます。 (しかし、ここではおそらく必要ありません。引用符がない項目のみを使用できます。結果である$matchStr
ため、単語の区切りについて心配する必要はありませんが、IFS=" " read -r
タブが含まれていることはほとんどありません...)
答え2
主に効果がなく、エラーがない理由に焦点を当てるには、次の手順を実行します。
perlExp=\'s/^/\\/home\\/test\\/alpha\\/"$fldr"\\//\'
rename --verbose "$perlExp" ${fullmatchStr}
perlExp
一重引用符を含む文字列に割り当てられ、渡されますperl
。's/^/\/home\/test\/alpha\/...\//'
これは、単一の文字列定数を含む有効なPerl式です。何の効果もないので、rename
変更は適用されません。
つまり、シェル変数に含まれる引用符は単なるデータであり、シェルは変数拡張結果を再解析しません。したがって、値に追加の引用符を追加しないでください。foo="'foo bar'"; echo "$foo"
印刷と同じ操作を実行する方法を検討してください。echo "'foo bar'"
'foo bar'
別の質問として、fullmatchStr=testfile\ -\ "$matchStr"\ -\ *.txt
変数が文字列に設定され、testfile - ... - *.txt
スペースとglob文字の両方が含まれている場合は、引用符なしで展開すると、最初に、、で区切られ、globが拡張され、ファイルtestfile
名-
で終わるすべて...
の項目と一致します。この問題を解決するには、空の文字列に設定して単語の区切りを無効にする必要があります。.
*.txt
.txt
IFS
Perlを使用するとs///
、さまざまな文字を区切り文字として使用することもできます。
したがって、次のように動作できます。
#!/bin/bash
# disable word splitting in the script due to the unquoted expansion
IFS=
# the IFS assignment below only applies to 'read'
while IFS=" " read -r fldr matchStr
do
perlExp='s#^#/tmp/test/alpha/'"$fldr"'/#'
fullmatchStr="testfile - $matchStr - *.txt"
rename --verbose "$perlExp" ${fullmatchStr}
done < test.dat
しかし正直、Muluのソリューション環境を介して文字列を渡す方法は、コードにデータを挿入するときに必要な注意と2段階の引用を避け、特にファイルからPerlにインポートされたディレクトリ名が何であるかを気にする必要がないため、より良い方法です。特殊文字が含まれています。