2017-08-21 21 views
0

私のbashスクリプトは、printfを使用して別のbashスクリプトを作成します。bashスクリプト内の変数のエスケープ

printf "#!/bin/bash 
HOME=${server} 
file=gromacs* 
file_name=\$(basename "\${file}") 
date=\$(date +"\%m_\%d_\%Y") 


for sim in \${HOME}/* ; do 
if [[ -d \$sim ]]; then 
    simulation=$(basename "\$sim") 
    pushd \${sim} 
    cp \$file \${server}/\${results}/\${file_name}.\${simulation}.\${date} 
    echo "\${file_name}\ from\ \${simulation}\ has\ been\ collected!" 
    popd 
fi 
done" > ${output}/collecter.sh 

は、ここでは、新しいバッシュの不完全になり、以下の部分が適切に

"\%m_\%d_\%Y" 

を動作しませんでした日付変数

date=\$(date +"\%m_\%d_\%Y") 

内の要素のescappiongに問題がありますprintfによって生成されたスクリプト。

どのように修正する必要がありますか?

ありがとうございます!

+0

最初に '%'をエスケープする必要はありません。 –

答えて

0

はあなたがesscaps、電子とprintfにエスケープする必要があり、この

date=\$(date +\"%%m_%%d_%%Y\")" 
1

試してみてください。 g:

date=\$(date +"%%m_%%d_%%Y") 

は、date=\$(date +"%m_%d_%Y")とする必要があります。しかし、その機能を使用していないので、printfは使用しないでください。代わりに、ファイルに文字列をcatできます:

cat > ${output}/collecter.sh <<'END' 
HOME=${server} 
... 
done 
END 

これは、多くのエスケープを避け、コードを読みやすくします。

+1

'printf '%s'" ... "> $ {output}/collecter.sh'と同じ効果を得ることができます。 – melpomene

+0

しかし、どのような利点がありますか? – clemens

+1

'<< END'は引用符で囲まれていないが、依然としてheredoc内で拡張を行います。その結果、まだ多くのエスケープが必要です。内容を完全にリテラルにするには、 '<< \ END'または' << 'END' 'でなければなりません。 –

3

引用したヘレドックを使用してください。

{ 
    ## print the header, and substitute our own value for HOME 
    printf '#!/bin/bash\nHOME=%q\n' "$server" 
    ## EVERYTHING BELOW HERE UNTIL THE EOF IS LITERAL 
    cat <<'EOF' 
file=(gromacs*) 
((${#file[@]} == 1)) && [[ -e $file ]] || { 
    echo "ERROR: Exactly one file starting with 'gromacs' should exist" >&2 
    exit 1 
} 
file_name=$(basename "${file}") 
date=$(date +"%m_%d_%Y") 

for sim in "$HOME"/* ; do 
if [[ -d $sim ]]; then 
    simulation=$(basename "$sim") 
    (cd "${sim}" && exec cp "$file" "${server}/${results}/${file_name}.${simulation}.${date}") 
    echo "${file_name} from ${simulation} has been collected!" 
fi 
done 
EOF 
} >"${output}/collecter.sh" 

注:引用ヒアドキュメント(cat <<'EOF')の内部

  • ない置換が行われないので、何のエスケープが必要とされません。このようにして、生成されたファイルにコードを正確に記述することができます。
  • コードを生成し、バック元の値に評価するように値をエスケープするprintf %qを使用します。(それは文字通り、一重引用符の内側に置換された場合の内容$(rm -rf ~)'$(rm -rf ~)'は、それらをエスケープするだろう作る、)それ以外の場合は、$(rm -rf ~)を含む変数は、与えられたコマンドが実行される可能性があります。
  • グローブ拡張は結果のリストを返します。その結果を格納する適切なデータ型は配列であり、文字列ではありません。はこのように、file=(gromacs*)は、明示的な配列の結果の保存、そして我々は複数の結果を持っている場合、両方のために、次のコードをチェックして、私たちの結果は、元のグロブ表現(何の一致を意味していないが存在していた)である場合になります。
  • 文字列分割を防ぐため、すべての展開を引用符で囲む必要があります。これは"$HOME"/*、ない$HOME/*を意味します - Windowsの由来あなたは/Users/Firstname Lastnameを持っているのプラットフォーム、またはあなたがマウントされたサイトを考える - ユーザーが空白を含むホームディレクトリを持っている(はい、この現象が発生したときにそうでなければ、問題があるでしょうホームディレクトリのボリュームも同じです)。
  • pushdおよびpopdは、スクリプトを作成するためのツールではなく、対話型の拡張機能です。外部プログラムの作成にはfork()操作が含まれており、サブシェル内でのディレクトリの変更はそのサブシェルが終了すると終了するため、サブシェルを生成することによってそのサブシェル内にcd ' exec組み込みルーチンを使用してサブシェルのPIDをcpのサブシステムのPIDに置き換えることによって、cpがその親として機能するシェルとは別のプロセスで起動できるようになったforkを防ぎます。
+0

バグは何ですか? – wjandrea

+0

ああ - バグではなく文章上の問題。それは「著者の意図との衝突」であり、「害を引き起こす」ではありません。謝罪。 –

+0

ああ、申し訳ありません。私はあなたが何を意味しているのか、なぜあなたがそれをやるのかを見ることができます。 – wjandrea

関連する問題