Lệnh Bash awk với dấu ngoặc kép


8

Tôi đã cố gắng tìm câu trả lời cho câu hỏi này trong một thời gian. Tôi đang viết một kịch bản nhanh để chạy một lệnh dựa trên đầu ra từ 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

Vấn đề là -cđối số có một lệnh trong các trích dẫn đơn và tôi không thể tìm ra cách thoát đúng và cũng $1không mở rộng thành tên người dùng.

Về cơ bản, tôi chỉ đang cố gắng đưa nó ra đầu ra:

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

Vân vân...



2
Vòng lặp của bạn chỉ lặp đi lặp lại một lần.
jordanm

@jordanm Tôi không nghĩ rằng điều đó áp dụng choawk
jesse_b

2
@Jlie_b vấn đề trích dẫn của anh ấy là trích dẫn shell, thực sự không có gì để làm với awk. Anh ta cần phải thoát khỏi dấu ngoặc đơn lồng nhau.
jordanm

Câu trả lời:


13

Để chạy lệnh xfs_quota -x -c 'limit bsoft=5g bhard=6g USER' /homecho từng USERngười có UID ít nhất $ID_minimum, trước tiên hãy xem xét phân tích những người dùng đó và sau đó thực sự chạy lệnh, thay vì cố gắng tạo một chuỗi đại diện cho lệnh mà bạn muốn chạy.

Nếu bạn tạo chuỗi lệnh, bạn sẽ phải có evalnó. Điều này là khó khăn và dễ dàng để có được sai. Tốt hơn là chỉ cần lấy một danh sách tên người dùng và sau đó chạy lệnh.

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

Lưu ý rằng không có nhu cầu thực sự cho các trích dẫn đơn xung quanh đối số sau -c. Ở đây tôi sử dụng dấu ngoặc kép vì tôi muốn shell mở rộng $userbiến chứa các giá trị được trích xuất bởi awk.

Tôi sử dụng ${ID_minimum:-1000}khi đưa giá trị cho minbiến trong awklệnh. Điều này sẽ mở rộng thành giá trị của $ID_minimumhoặc 1000nếu biến đó trống hoặc không được đặt.


Nếu bạn thực sự muốn, bạn có thể làm cho vòng lặp trên in ra các lệnh thay vì thực hiện chúng:

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

Một lần nữa lưu ý rằng việc sử dụng dấu ngoặc kép trong chuỗi lệnh được xuất ra (thay vì dấu ngoặc đơn) sẽ không gây nhầm lẫn cho trình bao theo bất kỳ cách nào nếu bạn thực hiện các lệnh được tạo bằng cách sử dụng evalhoặc mặc dù một số phương tiện khác. Nếu điều đó làm phiền bạn, chỉ cần hoán đổi dấu ngoặc đơn và dấu ngoặc kép trong đối số đầu tiên printfở trên.


1
câu trả lời duy nhất để sử dụng chính xác getent thay vì parse / etc / passwd.
cas

1
@cas macOS là một Unix không có getent(trên máy tính để bàn, dù sao) và câu hỏi không được gắn thẻ "linux."
TheDudeAdides

2
@TheDudeAdides Giả sử rằng người dùng đã chọn UID 1000 vì đó là điểm cắt giữa tài khoản dịch vụ hệ thống và tài khoản người dùng trên hệ thống của họ, chúng tôi có thể suy luận rằng người dùng này không chạy trên macOS (tài khoản người dùng đầu tiên là 501). Bên cạnh đó, việc sử dụng các tiện ích XFS trên macOS khá hiếm thấy vì XFS không thực sự được Apple hỗ trợ. Ngoài ra, /homekhông thực sự được sử dụng trên macOS (nó ở đó, nhưng thường trống).
Kusalananda

@Kusalananda Điểm lấy. Tôi đã bị mù với phần XFS.
TheDudeAdides

1
@TheDudeAdides getent không chỉ dành cho linux. nó cũng có trên netbsd, freebsd và solaris.
cas

6
for f in /etc/passwd;

Điều này hơi ngớ ngẩn vì thực sự không có vòng lặp chỉ với một giá trị.

Nhưng vấn đề dường như là in dấu ngoặc đơn từ awk. Bạn có thể thoát chúng trong trình bao, nhưng bạn cũng có thể sử dụng dấu gạch chéo ngược trong awk để in chúng. là nhân vật với số giá trị OOO (trong bát phân ), do đó là . Vì vậy, đây sẽ là một cách để làm điều đó:\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

Bạn có thể sử dụng lối thoát tương tự trong hex, \x27nhưng nó có thể bị hiểu sai trong một số triển khai nếu ký tự sau là một chữ số thập lục phân hợp lệ. (Và tất nhiên tôi giả sử ASCII hoặc bộ ký tự tương thích ASCII, ví dụ UTF-8.)


Lưu ý rằng ở đây lkhông phải là một chữ số hex, nhưng "\x27df\n"sẽ được coi là "\x27" "df"trong busybox awk hoặc mawk mà là "\xdf"( 0x27dfcast thành 8 bit char) trong awk và gawk ban đầu (đó là lý do tại sao POSIX không chỉ định \xHH). Octals ( \047) không phải là vấn đề tương tự và là POSIX. Trong mọi trường hợp, điều đó giả định một hệ thống ASCII (một giả định hợp lý ngày nay).
Stéphane Chazelas

6

Sử dụng -f -tùy chọn awk để lấy tập lệnh từ stdin và tài liệu ở đây:

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

Điều này đã làm nó.

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, vâng, bạn không thể đặt các trích dẫn đơn trong một chuỗi trích dẫn đơn, vì vậy bạn phải kết thúc nó trước, sau đó trích dẫn một trích dẫn bạn muốn chèn. Nó không quan trọng nếu bạn làm '\''hoặc '"'"'. Cả hai làm việc, cả hai trông khó chịu.
ilkkachu

@OP, hy vọng bạn thấy bình luận của jordanm về vòng lặp của bạn là không cần thiết. Vòng lặp của bạn chỉ chạy một lần nên không khác gì chạy đơn giản cùng một lệnh awk bên ngoài vòng lặp.
jesse_b

1

Đó là một nhà nghỉ tồi tệ, nhưng nó nhanh chóng và dễ dàng ...

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

Đây dường như là một cơ hội lý tưởng để thuê bạn một số xargs(hoặc 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

Lợi thế của việc sử dụng xargshoặc parallellà bạn có thể chỉ cần loại bỏecho khi bạn sẵn sàng chạy lệnh thực sự (có thể thay thế bằng sudo, nếu cần thiết).

Bạn cũng có thể sử dụng các tùy chọn -p/ / --interactive( tiện ích này chỉ dành cho GNU) hoặc --dry-run( parallelchỉ), để xác nhận trước khi chạy từng tiện ích hoặc chỉ để xem những gì sẽ chạy, trước khi bạn chạy nó.

Phương thức chung được sử dụng ở trên sẽ hoạt động trên hầu hết các Unix và không yêu cầu xargstùy chọn dành riêng cho GNU . Các dấu ngoặc kép làm cần phải "trốn" vì vậy mà chúng xuất hiện theo nghĩa đen ở đầu ra. Lưu ý rằng "chuỗi thay thế" {}, trong xargs -I{}có thể là bất cứ điều gì bạn thích và -Ingụ ý -L1(chạy một lệnh trên mỗi dòng đầu vào thay vì gộp chúng lại).

GNU Parallel không yêu cầu -Itùy chọn ( {}là chuỗi thay thế mặc định), và cung cấp cho bạn tiền thưởng ngay lập tức chạy nhiều công việc song song, thậm chí nếu bạn không muốn làm phiền tìm hiểu về bất kỳ của khác các tính năng .

Là một mặt lưu ý, tôi thậm chí không chắc chắn nếu xfs_quota's -clựa chọn là nghĩa vụ phải được sử dụng như thế này, mặc dù tôi không có XFS hệ thống tập tin thuận tiện để kiểm tra. Bạn có thể thậm chí không cần phải xử lý chuỗi được trích dẫn ở vị trí đầu tiên (trừ khi bạn mong đợi tên người dùng có khoảng trắng trong đó, theo tôi đoán là có thể), vì có vẻ như bạn có thể đưa ra nhiều -ctùy chọn trên cùng một dòng lệnh, theo đến trang man kèm theo xfsprogs4.5.s Something.


0

Với GNU Parallel bạn có thể làm:

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

Giải trình:

--colsep :Split on:
-qkhông phân chia lệnh trên dấu cách (giữ '...' dưới dạng một chuỗi)
{=1 ... =}Đánh giá biểu thức perl này trên đối số đầu tiên của dòng
$_ eq "nfsnobody" and skip();Nếu giá trị == nfsnobody: Skip
$arg[3] <= 1000 and skip();If argument3 <= 1000: Skip

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.