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
您的循环只会重复一次。
jordanm

@jordanm我认为这并不适用awk
jesse_b

2
@Jesse_b他的报价问题是外壳报价,与awk并没有任何关系。他需要转义嵌套的单引号。
jordanm

Answers:


13

xfs_quota -x -c 'limit bsoft=5g bhard=6g USER' /homeUSERUID至少为每个$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。在这里我使用双引号,因为我希望shell扩展$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或通过某种其他方式执行生成的命令,则在输出的命令字符串中使用双引号(而不是单引号)不会以任何方式混淆shell 。如果让您感到困扰,只需在printf上面的第一个参数中将单引号和双引号引起来。


1
正确使用getent而不是解析/ etc / passwd的唯一答案。
cas

1
@cas macOS是不带Unix的Unix getent(无论如何,在桌面上),问题未标记为“ linux”。
TheDudeAbides

2
@TheDudeAbides假设用户之所以选择UID 1000,是因为它是系统服务帐户和其系统上的用户帐户之间的截止日期,我们可以推断出该用户未在macOS上运行(第一个用户帐户为501)。此外,由于苹果并未真正支持XFS,因此在macOS上使用XFS实用程序并不常见。另外,/home在macOS上并没有真正使用它(它在那里,但通常为空)。
库萨兰达

@Kusalananda点。我对XFS部分视而不见。
TheDudeAbides

1
@TheDudeAbides getent不仅限于Linux。至少在netbsd,freebsd和solaris上也是如此。
cas

6
for f in /etc/passwd;

这有点傻,因为实际上只有一个值就没有循环。

但问题似乎是从awk打印单引号。您可以在外壳中转义它们,但是也可以在awk中使用反斜杠转义符来打印它们。是具有数字值OOO八进制)的字符,因此也是。因此,这将是一种实现方法:\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

您可以在十六进制中使用类似的转义\x27符,但是如果以下字符是有效的十六进制数字,则在某些实现中可能会误解。(当然,我假设使用ASCII或与ASCII兼容的字符集,例如UTF-8。)


请注意,这里l不是十六进制数字,这很好,但是在原始awk和gawk "\x27df\n"中将"\x27" "df"在busybox awk或mawk中将其视为"\xdf"(强制转换为0x27df8位字符)(这就是POSIX未指定的原因\xHH)。八进制(\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

使用xargs或的优点parallel是,您可以在准备好实际运行命令时将其删除echosudo如果需要,可以将其替换为)。

您也可以使用这些工具-p/ --interactive(后者是GNU-只),或--dry-runparallel只)选项,运行每一个,或者只是为了看看之前得到确认运行,在运行之前。

上面使用的通用方法应该在大多数Unix上都可以使用,并且不需要GNU特定的xargs选项。双引号确实需要“转义”,以使它们在输出中实际显示。请注意,“替换字符串” {}in xargs -I{}可以是您喜欢的任何-I含义,并且可以暗示-L1(每输入行运行一个命令,而不是对其进行批处理)。

GNU并行不需要-I选项({}是默认替换字符串),并为您提供并行运行大量的就业机会,即使你不想打扰学习的即时奖金任何 它的 其他 功能

附带说明一下,尽管我没有方便测试的XFS文件系统,但我什至不确定是否应该像这样使用xfs_quota-c选项。您可能甚至根本不需要用引号引起来的字符串(除非您希望用户名中包含空格,我想这是可能的),因为看起来您可以-c在同一命令行上提供多个选项,到xfsprogs4.5.something 附带的手册页。


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();如果value == nfsnobody:跳过
$arg[3] <= 1000 and skip();如果arguments3 <= 1000:跳过

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.