web-dev-qa-db-ja.com

引用符付きのbash awkコマンド

私はしばらくの間、この質問に対する答えを見つけようと努めてきました。 awkからの出力に基づいてコマンドを実行する簡単なスクリプトを書いています。

ID_minimum=1000
for f in /etc/passwd;
do 
    awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c 'limit bsoft=5g bhard=6g $1' /home "}' $f;       
done

問題は、-c引数が一重引用符で囲まれたコマンドを受け取り、それを適切にエスケープする方法を理解できず、$1がユーザー名に展開されないことです。

基本的に私はそれを出力させようとしているだけです:

xfs_quota -x -c 'limit bsoft=5g bhard=6g userone' /home
xfs_quota -x -c 'limit bsoft=5g bhard=6g usertwo' /home

等...

8
ZCT

UIDがxfs_quota -x -c 'limit bsoft=5g bhard=6g USER' /homeであるUSERごとにコマンド$ID_minimumを実行するには、これらのユーザーを解析してから、実際にrunコマンドではなく、実行するコマンドを表す文字列を作成しようとしています。

コマンド文字列を作成する場合は、evalにする必要があります。これは手間がかかり、間違いやすいです。ユーザー名のリストを取得して、コマンドを実行することをお勧めします。

getent passwd |
awk -F: -v min="${ID_minimum:-1000}" '$3 >= min && $1 != "nfsnobody" { print $1 }' |
while IFS= read -r user; do
    xfs_quota -x -c "limit bsoft=5g bhard=6g $user" /home
done

-cの後に引数を囲む引用符singleは実際には必要ありません。ここでは、シェルでawkによって抽出された値を含む$user変数を展開するため、二重引用符を使用しています。

minコマンドでawk変数に値を与えるときに${ID_minimum:-1000}を使用します。これは$ID_minimumの値に、またはその変数が空であるか設定されていない場合は1000に展開されます。


reallyを使用したい場合は、上記のループでコマンドを実行する代わりにコマンドを出力することができます。

getent passwd |
awk -F: -v min="${ID_minimum:-1000}" '$3 >= min && $1 != "nfsnobody" { print $1 }' |
while IFS= read -r user; do
    printf 'xfs_quota -x -c "limit bsoft=5g bhard=6g %s" /home\n' "$user"
done

生成されたコマンドをevalを使用して実行する場合や他の手段を使用する場合でも、出力されるコマンド文字列で(単一引用符の代わりに)二重引用符を使用してもシェルが混乱することはありません。気になる場合は、上記のprintfの最初の引数で一重引用符と二重引用符を入れ替えてください。

13
Kusalananda
for f in /etc/passwd;

値が1つだけのループは実際にはないため、これは少しばかげています。

しかし、問題はawkからの単一引用符の印刷にあるようです。シェルでエスケープすることもできますが、awk内でバックスラッシュエスケープを使用して印刷することもできます。 \OOOは数値を持つ文字です OOOoctal)なので、\047'。したがって、これはそれを行う1つの方法です。

awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" {
    printf "xfs_quota -x -c \047limit bsoft=5g bhard=6g %s\047 /home\n", $1}' /etc/passwd

16進数で同様のエスケープを使用できます、\x27、ただし、次の文字が有効な16進数である場合、一部の実装で誤って解釈される可能性があります。 (そしてもちろん、ASCIIまたはASCII互換の文字セット、たとえばUTF-8と仮定しました。)

6
ilkkachu

使用 -f - awkのオプションで、標準入力とヒアドキュメントからスクリプトを取得します。

awk -F: -v "ID=$ID_minimum" -f - <<'EOT' /etc/passwd
$3>=1000 && $1!="nfsnobody" {
    print "xfs_quota -x -c 'limit bsoft=5g bhard=6g "$1"' /home "
}
EOT
6
mosvy

これで終わりました。

awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c '"'"'limit bsoft=5g bhard=6g ''"$1"'''"'"' /home "}' /etc/passwd
2
ZCT

これは、xargs(または GNU Parallel )を採用する理想的な機会のように思えます。

getent passwd \
  | awk -F: '$3>=1000 && $1!="nfsnobody" {print $1}' \
  | xargs -I{} \
      echo xfs_quota -x -c \"limit bsoft=5g bhard=6g {}\" /home

# output:
# xfs_quota -x -c "limit bsoft=5g bhard=6g userone" /home
# xfs_quota -x -c "limit bsoft=5g bhard=6g usertwo" /home

xargsまたはparallelを使用する利点は、実際にコマンドを実行する準備ができたときにechoを簡単に削除できることです(おそらく必要に応じて、Sudoに置き換えます)。

これらのユーティリティの-p/--interactive(後者はGNUのみ)または--dry-runparallelのみ)オプションを使用して、それぞれを実行する前に確認を取得することもできます。 、または単に would を実行する前に、実行内容を確認します。

上記で使用されている一般的な方法は、ほとんどのUnixで機能し、GNU固有のxargsオプションを必要としません。二重引用符 do は、出力に文字どおり表示されるように「エスケープ」する必要があります。 {}の「置換文字列」xargs -I{}は任意の名前にすることができ、-I-L1を意味します(バッチ処理するのではなく、入力行ごとに1つのコマンドを実行します) )。

GNU Parallelは-Iオプション({}がデフォルトの置換文字列)を必要とせず、多くのジョブを並行して実行することですぐにボーナスを得ることができます。 anyofitsotherfeatures

補足として、xfs_quota-cオプションをこのように使用することになっているのかどうかさえわかりませんが、XFSファイルシステムをテストするのに便利ではありません。最初に引用符で囲まれた文字列を処理するために needed する必要がないかもしれません(おそらく空白にユーザー名が含まれていると思われる場合を除きます)。 xfsprogs 4.5.somethingに含まれているmanページによれば、同じコマンドラインで複数の-cオプションを指定できます。

1
TheDudeAbides

それはひどい小屋です、しかしそれは速くて簡単です...

awk -F: -vQ="'" -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c " Q "limit bsoft=5g bhard=6g $1" Q " /home "}' $f;       
1
Grump

GNU Parallelを使用すると、次のことができます。

_getent passwd |
  parallel --colsep : -q xfs_quota -x -c \
    'limit bsoft=5g bhard=6g {=1 $_ eq "nfsnobody" and skip(); $arg[3] <= 1000 and skip();  =}' /home
_

説明:

_--colsep :_分割:
_-q_コマンドをスペースで分割しません( '...'を単一の文字列として保持します)
_{=1 ... =}_行の最初の引数でこのPerl式を評価します
$_ eq "nfsnobody" and skip();値== nfsnobodyの場合:スキップ
$arg[3] <= 1000 and skip(); argument3 <= 1000の場合:スキップ

0
Ole Tange