私はしばらくの間、この質問に対する答えを見つけようと努めてきました。 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
等...
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
の最初の引数で一重引用符と二重引用符を入れ替えてください。
for f in /etc/passwd;
値が1つだけのループは実際にはないため、これは少しばかげています。
しかし、問題はawkからの単一引用符の印刷にあるようです。シェルでエスケープすることもできますが、awk内でバックスラッシュエスケープを使用して印刷することもできます。 \OOO
は数値を持つ文字です OOO (octal)なので、\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と仮定しました。)
使用 -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
これで終わりました。
awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c '"'"'limit bsoft=5g bhard=6g ''"$1"'''"'"' /home "}' /etc/passwd
これは、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-run
(parallel
のみ)オプションを使用して、それぞれを実行する前に確認を取得することもできます。 、または単に 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
オプションを指定できます。
それはひどい小屋です、しかしそれは速くて簡単です...
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;
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の場合:スキップ