Làm thế nào để bỏ qua các lệnh xargs nếu đầu vào stdin trống?


188

Hãy xem xét lệnh này:

ls /mydir/*.txt | xargs chown root

Mục đích là để thay đổi chủ sở hữu của tất cả các tệp văn bản trong mydirroot

Vấn đề là nếu không có .txttệp nào trong mydirđó thì xargs sẽ báo lỗi rằng không có đường dẫn nào được chỉ định. Đây là một ví dụ vô hại vì một lỗi đang được đưa ra, nhưng trong một số trường hợp, như trong kịch bản mà tôi cần sử dụng ở đây, một đường dẫn trống được coi là thư mục hiện tại. Vì vậy, nếu tôi chạy lệnh /home/tom/đó từ đó nếu không có kết quả ls /mydir/*.txtvà tất cả các tệp theo /home/tom/chủ sở hữu của chúng đã thay đổi thành root.

Vì vậy, làm thế nào tôi có thể có xargs bỏ qua một kết quả trống?


6
Ngoài ra: Không bao giờ đầu ra đường ống từ lsđể sử dụng lập trình; xem mywiki.wooledge.org/ParsingLs
Charles Duffy

Trường hợp sử dụng của tôi là git branch --merged | grep -v '^* ' | xargs git branch -d, cũng không thành công với đầu vào trống
belkka

Câu trả lời:


305

Đối với GNU xargs, bạn có thể sử dụng -rhoặc --no-run-if-emptytùy chọn:

--no-run-if-empty
-r
Nếu đầu vào tiêu chuẩn không chứa bất kỳ khoảng trống nào, không chạy lệnh. Thông thường, lệnh được chạy một lần ngay cả khi không có đầu vào. Tùy chọn này là một phần mở rộng GNU.


9
Tôi thành thật tìm kiếm trên google trước và tìm thấy cái này (nhưng sẽ không hỏi nếu không đọc hướng dẫn). Tôi nghĩ rằng đây có thể là hành vi mặc định, không cần một công tắc để kích hoạt nó.
JonnyJD

28
Thật không may, điều này không hoạt động trên máy Mac. Không phải là ngắn cũng không phải là lựa chọn dài.
wedi

9
@wedi - OSX có không gian người dùng BSD, vì vậy điều này sẽ xảy ra thường xuyên. Bạn có thể làm việc xung quanh nó bằng cách sử dụng homebrew để cài đặt tập tin GNU.
edk750

7
Bạn có ý nghĩa brew install findutils. findutils, Không fileutils.
Mitar

3
Trên macOS, việc không cung cấp cờ dẫn đến việc không chạy lệnh nào, vì vậy tôi đoán nó không cần thiết.
Trejkaz

15

Người sử dụng xargs phi GNU có thể tận dụng -L <#lines>, -n <#args>, -i, và -I <string>:

ls /empty_dir/ | xargs -n10 chown root # chown executed every 10 args or fewer
ls /empty_dir/ | xargs -L10 chown root # chown executed every 10 lines or fewer
ls /empty_dir/ | xargs -i cp {} {}.bak # every {} is replaced with the args from one input line
ls /empty_dir/ | xargs -I ARG cp ARG ARG.bak # like -i, with a user-specified placeholder

Hãy nhớ rằng xargs chia dòng ở khoảng trắng nhưng trích dẫn và thoát có sẵn; RTFM để biết chi tiết.

Ngoài ra, như Doron Behar đề cập, cách giải quyết này không khả chuyển nên có thể cần kiểm tra:

$ uname -is
SunOS sun4v
$ xargs -n1 echo blah < /dev/null
$ uname -is
Linux x86_64
$ xargs --version | head -1
xargs (GNU findutils) 4.7.0-git
$ xargs -n1 echo blah < /dev/null
blah

2
Dường như không trả lời câu hỏi?
Nicolas Raoul

2
@Nicolas: đó là cách để ngăn chặn việc thực thi nếu phiên bản của xargsbạn không hỗ trợ --no-run-if-emptychuyển đổi và cố gắng chạy đối số lệnh mà không có đầu vào STDIN (đây là trường hợp trong Solaris 10). Các phiên bản trong các unix khác có thể bỏ qua một danh sách trống (ví dụ AIX).
arielCo

4
Đúng! Trên MAC OSX, echo '' | xargs -n1 echo blahkhông làm gì cả; trong khi echo 'x' | xargs -n1 echo blahin "blah".
carlosayam

2
Đối với cả hỗ trợ GNU và BSD / OSX, tôi kết thúc bằng cách sử dụng cái gì đó như : ls /mydir/*.txt | xargs -n 1 -I {} chown root {}, như câu trả lời này cho thấy.
Luís Bianchin

2
Cần lưu ý rằng echo '' | xargs -n1 echo blahcác bản in blahvới GNU xargs.
Doron Behar

11

man xargsnói --no-run-if-empty.


1
Nếu bạn đang dùng OSX thì không. edk750 đã giải thích điều này trong bình luận của họ nếu bạn tò mò tại sao.
jasonleonhard

9

Về mặt xargs, bạn có thể sử dụng -rnhư được đề xuất, tuy nhiên nó không được BSD hỗ trợ xargs.

Vì vậy, như cách giải quyết, bạn có thể vượt qua một số tệp tạm thời bổ sung, ví dụ:

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root $(mktemp)

hoặc chuyển hướng stderr của nó thành null ( 2> /dev/null), vd

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root 2> /dev/null || true

Một cách tiếp cận khác tốt hơn là lặp lại các tệp được tìm thấy bằng whilevòng lặp:

find /mydir -type f -name "*.txt" -print0 | while IFS= read -r -d '' file; do
  chown -v root "$file"
done

Xem thêm: Bỏ qua kết quả trống cho xargs trong Mac OS X


Ngoài ra, xin lưu ý rằng phương pháp thay đổi quyền của bạn không tốt và nó không được khuyến khích. Chắc chắn bạn không nên phân tích đầu ra của lslệnh (xem: Tại sao bạn không nên phân tích đầu ra của ls ). Đặc biệt là khi bạn đang chạy lệnh của bạn bằng root, vì các tệp của bạn có thể chứa các ký tự đặc biệt có thể được hiểu bởi trình bao hoặc tưởng tượng tệp có ký tự khoảng trắng xung quanh /, thì kết quả có thể rất tệ.

Do đó, bạn nên thay đổi cách tiếp cận và sử dụng findlệnh thay thế, vd

find /mydir -type f -name "*.txt" -execdir chown root {} ';'

1
Xin lỗi, tôi không hiểu làm thế nào while IFS= read -r -d '' filelà hợp lệ. Bạn có thể giải thích?
Adrian

2

Trên OSX: Bash thực hiện lại xargsviệc xử lý -rđối số, đặt ví dụ đó vào $HOME/binvà thêm nó vào PATH:

#!/bin/bash
stdin=$(cat <&0)
if [[ $1 == "-r" ]] || [[ $1 == "--no-run-if-empty" ]]
then
    # shift the arguments to get rid of the "-r" that is not valid on OSX
    shift
    # wc -l return some whitespaces, let's get rid of them with tr
    linecount=$(echo $stdin | grep -v "^$" | wc -l | tr -d '[:space:]') 
    if [ "x$linecount" = "x0" ]
    then
      exit 0
    fi
fi

# grep returns an error code for no matching lines, so only activate error checks from here
set -e
set -o pipefail
echo $stdin | /usr/bin/xargs $@

2

Đây là hành vi của GNU xargs có thể được nén bằng cách sử dụng -r, --no-run-if-blank.

Biến thể * BSD của xargs mặc định có hành vi này, vì vậy -r là không cần thiết. Kể từ FreeBSD 7.1 (phát hành vào tháng 1 năm 2009), một đối số -r được chấp nhận (phù thủy không làm gì) vì lý do tương thích.

Cá nhân tôi thích sử dụng longopts trong các tập lệnh nhưng vì xargs * BSD không sử dụng longopts nên chỉ sử dụng "-r" và xargs sẽ hoạt động tương tự trên * BSD và trên các hệ thống linux

xargs trên MacOS (hiện tại là MacOS Mojave) đáng buồn là không hỗ trợ đối số "-r".

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.