Friday, 26 December 2014

[CLI] xargs - câu lệnh để xây dựng câu lệnh

Trên UNIX-like OS, không phải câu lệnh nào cũng nhận đầu vào qua stdin, và như thế có vẻ không tiện lắm để output của câu lệnh trước làm input cho câu lệnh sau - một tính năng tuyệt vời "pipeline" của shell.

grep có thể lấy output của chương trình khác thành input của mình, ví dụ:
$ ps -fU root | grep -E '[g]etty|^UID'
UID        PID  PPID  C STIME TTY          TIME CMD
root       850     1  0 17:19 tty4     00:00:00 /sbin/getty -8 38400 tty4
root       855     1  0 17:19 tty5     00:00:00 /sbin/getty -8 38400 tty5
root       864     1  0 17:19 tty2     00:00:00 /sbin/getty -8 38400 tty2
root       865     1  0 17:19 tty3     00:00:00 /sbin/getty -8 38400 tty3
root       868     1  0 17:19 tty6     00:00:00 /sbin/getty -8 38400 tty6
-f : hiển thị khá đầy đủ các loại thông tin
-U: chỉ hiển thị các process được chạy bởi user có tên được chỉ định

Thế nhưng rm, cp, mv, printf hay nhiều câu lệnh khác không có tính năng đọc từ stdin, vì vậy xargs xuất hiện như giải pháp hoàn hảo để giải quyết vấn đề này.

Ví dụ sau sẽ in ra tất cả các user trên máy, sử dụng ký tự "__" để phân cách các user:
$  cut -d: -f1 /etc/passwd | xargs printf '%s__'; echo
root__daemon__bin__sys__sync__games__man__lp__mail__news__uucp__proxy__www-data__backup__list__irc__gnats__nobody__libuuid__syslog__messagebus__whoopsie__landscape__sshd__hvn__postfix__amavis__clamav__nagios__dovecot__dovenull__dovecot-agent__openldap__

Khi không gán câu lệnh nào cho xargs, nó sẽ chạy lệnh "echo"với argument là những gì nó nhận được từ stdin. Các "item" từ stdin được phân cách với nhau bởi các ký tự trống (space, tab) hay ký tự xuống dòng, vì vậy xargs được dùng như một giải pháp của bài toán nối dòng kinh điển :)).

Ví dụ copy các file có đuôi ``.py`` đến thư mục /tmp:
$ ls *.py | xargs cp --target-directory /tmp  #  hoặc -t cho ngắn
Tất nhiên bạn hoàn toàn có thể bỏ qua phần ls *.py | xargs mà chỉ cần:
$ cp *.py /tmp
cũng thu được kết quả giống hệt, ở đây chỉ sử dụng lệnh ls làm ví dụ, bạn có thể thấy nó hữu ích trong những trược hợp sử dụng một chuổi câu lệnh để lọc ra filename. Ví dụ sau copy tất cả các file chạy (có chứa từ ``bin/`` trong path) hiện đang chạy trên hệ thống vào thư mục /tmp/binnow:
$ mkdir /tmp/binnow && lsof | awk '{print $9}' | grep 'bin/' | sort | uniq | xargs cp -t /tmp/binnow
Còn làm gì sau đó thì tuỳ trí tưởng tượng của bạn.

xargs còn một tính năng rất mạnh mẽ khác là giúp chạy song song các câu lệnh nhưng không nằm trong nội dung bài viết này.Các option khác để xử lý các trường hợp đặc biệt có thể xem thêm trong ``man 1 xargs``.

Đến đây, hãy nhớ rằng: xargs là một chương trình giúp xây dựng và chạy câu lệnh từ stdin, đặc biệt thích hợp để trợ giúp các chương trình không nhận đầu vào từ stdin.
$ whatis xargs
xargs (1)            - build and execute command lines from standard input
$ dpkg -S `which xargs`
findutils: /usr/bin/xargs
xargs là một chương trình đi kèm với câu lệnh "find"
$ dpkg -L findutils | grep bin/ # liệt kê các câu lệnh mà gói findutils cung cấp
/usr/bin/xargs
/usr/bin/find
/usr/bin/oldfind
Xem thêm các câu lệnh tìm hiểu package ở đây.
Bài viết thực hiện trên:
$ lsb_release -d
Description:    Ubuntu 12.04.4 LTS
$ xargs --version | grep '^xargs'
xargs (GNU findutils) 4.4.2
Hết.
hvn@familug.org

Xem danh sách các câu lệnh phổ biến / hữu ích trên Linux: http://www.familug.org/2014/11/cmd-linux-utilities-co-gi-can-thiet.html