Bashとqmakeの変数の二重引用符をエスケープします。

Bashとqmakeの変数の二重引用符をエスケープします。

私は、次のサブディレクトリにすべてのファイルをビルドすることを目的としたqmake用の次のメタプロジェクトファイルを作成しました.pro(ファイル名は、歴史的な理由やその他のツールチェーンのためにフォルダ名と一致しません)。本質的に、これは次のようになります(プロジェクト固有の項目は取り除きます)。

THIS_FILE=make-all.pro
TEMPLATE=subdirs
FIND= "find -name \'*.pro\' -printf \'%p\\n\'"
AWK= "awk \'{$1=substr($1,3); print}\'"
RMTHIS= "awk \'!/$$THIS_FILE/\'"
SUBDIRS= $$system($$FIND | $$AWK | $$RMTHIS )

.proBashスクリプトからファイルを含むフォルダのリストを取得する必要があるため、メソッドをコピーすることにしました。

#!/bin/bash
FIND="find -name '*.pro' -printf '%h\\n"
AWK="awk '{\$1=substr(\$1,3);printf}'"
SUBDIRS=$($FIND | $AWK)

明らかにこれはうまくいきません。awk表現にエラーがあります。invalid char ''Bashで同じ行を直接実行しようとすると、awk二重引用符を使用した場合にのみ実際に機能することがわかります。

find -name '*.pro' | awk "{\$1=substr(\$1,3);printf}"

問題のある行を次に変更してください。

AWK='awk "{$1=substr($1,3);printf}" '

手動で入力したコマンドの出力とは異なり、操作結果は提供されず、スクリプト出力は空です。確かに

 find -name '*.pro' 

スクリプトは現在のフォルダでのみファイルを見つけることができますが、bashコマンドラインはそのファイルをサブフォルダで見つけます。何が間違っていて、qmakeも異なる動作をするのはなぜですか?

答え1

私はqmake構文に慣れていませんが、あなたの例ではシェルとは非常に異なる方法で引用符と変数を使用します。したがって、同じコードを使用することはできません。

http://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-charactersあなたが知っておくべきことを扱ったので、ここであなたの質問に関連するものをまとめます。

つまり、単にシェルコマンドを文字列に入れることはできません。 Bashのようなシェルは文字列を再帰的に解析しません。ソースコードを解析し、次のようにコマンドを作成します。リスト文字列:単純なコマンドは、コマンド名(実行ファイルパス、関数名など)とその引数で構成されています。

このような割り当てを使用すると、AWK="awk '{\$1=substr(\$1,3);printf}'"変数は文字列に設定されます。変数を外部二重引用符AWKとして使用すると、変数の値を空白に解析して文字列リストに変換しますが、これをシェルに解析しません。$AWKコードなので、このような文字は'実際にはパラメータに含まれています。これはほとんど必要ないため、シェルで変数を使用するための一般的なアドバイスは次のとおりです。変数拡張の周囲に二重引用符を追加するそれらをあきらめるべきであることを知らないし、それが何を意味するか理解しない限り。 (ここの回答が全内容を教えてくれないことをご参考ください。)

Bashでは、単純なコマンドで配列を埋めることができます。

#!/bin/bash
FIND=(find -name '*.pro' -printf '%h\\n')
AWK=(awk '{$1=substr($1,3);print}')
SUBDIRS=$("${FIND[@]}" | "${AWK[@]}") 

しかし、コマンドを保存する最善の方法は、関数を定義することです。これは単純なコマンドに限定されない。この方法では、リダイレクト、変数の割り当て、条件などを実行できます。

#!/bin/bash
find_pro_files () {
  find -name '*.pro' -printf '%h\\n'
}
truncate_names () {
  awk '{$1=substr($1,3);print}'
}
SUBDIRS=$(find_pro_files | truncate_names)

このスクリプトで何をしたいのかはわかりませんが、特にコードスニペット間でfindコードawkを変更することを検討している場合は、おそらくより良い方法があります。ループを使用して、*.pro現在のディレクトリのサブディレクトリにあるファイルを繰り返すことができます。

for pro in */*.pro; do
  subdir="${pro%/*}"
  basename="${pro##*/}"
done

サブディレクトリを再帰的に巡回するには、bashで代わりに**/*.pro使用できますが、*/*.proこの方法はディレクトリへのシンボリックリンクとしても再帰されることに注意してください。

答え2

拡張子を持つファイルが直接含まれているディレクトリを見つけるだけで、xargsとオプションで.prouniqを使用できます。

find -type f -name "*.pro" | xargs -I{} dirname {} | uniq

相対パスの代わりに絶対パスが必要な場合:

find -type f -name "*.pro" | xargs -I{} dirname {} | xargs readlink -f | uniq

あなたの要件に応じてawkを使用するソリューションは次のとおりです。

find -type f -name "*.pro" | awk -F/ 'BEGIN { OFS="/" } { $1=""; $NF=""; print $0 }' | cut -c 2- | uniq

これが接頭辞のない相対パスです./

関連情報