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


8

私はしばらくの間、この質問に対する答えを見つけようと努めてきました。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

等...



2
ループは1回だけ繰り返されます。
ヨルダン

@jordanm当てはまらないと思いますawk
jesse_b

2
@Jesse_b彼の引用の問題はシェルの引用であり、awkとは何の関係もありません。彼はネストされた一重引用符をエスケープする必要があります。
ヨルダン

回答:


13

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

コマンド文字列を作成する場合は、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。ここでは二重引用符を使用してい$userます。これは、によって抽出された値を含む変数をシェルで展開するためですawk

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


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

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上記の最初の引数で一重引用符と二重引用符を入れ替えてください。


1
/ etc / passwdを解析するのではなく、getentを正しく使用するための唯一の答えです。
cas

1
@cas macOSはUnix getent(とにかくデスクトップ上)がないUnixであり、質問には「linux」というタグは付けられていません。
TheDudeAbides

2
@TheDudeAbidesシステムサービスアカウントとシステム上のユーザーアカウントの間のカットオフであるため、ユーザーがUID 1000を選択したと仮定すると、このユーザーはmacOSで実行されていないと推測できます(最初のユーザーアカウントは501)。さらに、XFSは実際にはAppleでサポートされていないため、macOSでXFSユーティリティを使用することはほとんどありません。また、/homemacOSでは実際には使用されません(存在しますが、通常は空です)。
クサラナンダ

@Kusalanandaポイント獲得。私はXFSの部分を知らなかった。
TheDudeAbides

1
@TheDudeAbides getentはLinux専用ではありません。少なくともnetbsd、freebsd、solarisにもあります。
cas

6
for f in /etc/passwd;

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

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

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など)を想定しています。)


ここでの罰金は、などという注意l16進数ではなく、"\x27df\n"として扱われる"\x27" "df"のbusyboxのawkのかのmawkではなくて"\xdf"0x27dfオリジナルのawkとgawkの8ビットのcharへのキャスト)(つまり、POSIXが指定されていない理由です\xHH)。8進数(\047)は同じ問題ではなく、POSIXです。いずれにせよ、これはASCIIシステムを想定しています(最近では妥当な想定です)。
ステファンChazelas

6

-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

2

これで終わりました。

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

1
@Jesse_b、はい、一重引用符で囲まれた文字列の中に一重引用符を入れることはできないため、最初にそれを終了してから、挿入する一重引用符を引用する必要があります。'\''またはかどうかは関係ありません'"'"'。どちらも機能し、どちらも煩わしく見えます。
ilkkachu

@OP、うまくいけば、ループが不要であるというjordanmのコメントを見た。ループは一度しか実行されないため、ループの外で同じawkコマンドを実行するだけと変わりません。
jesse_b

1

それはひどい小屋ですが、素早く簡単です...

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

これは私にあなたにいくつか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

xargsor を使用する利点parallelは、実際にコマンドを実行する準備ができechoときにを削除できることです(sudo必要に応じて、コマンドをに置き換えることができます)。

また、これらのユーティリティを使用することができます-p/ --interactiveまたは(後者はGNU-のみです)--dry-runparallel各1を実行する前に確認を得るために、または単にものを見るために、唯一の)選択肢でしょうあなたはそれを実行する前に、実行します。

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

GNUパラレルは必要としない-Iオプションを({}デフォルトの置換文字列である)、そしてあなたはについての学習を気にしたくない場合でも、あなたに並列に多くのジョブを実行しているのインスタントボーナスを与えるいかなる その ほか の特徴

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


0

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();しますif value == nfsnobody:skip
$arg[3] <= 1000 and skip();If argument3 <= 1000:skip

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.