Cách sử dụng `/ usr / bin / env sed -f` trong shebang?


25

/usr/bin/env sed -fvào các công trình đầu cuối.

Nhưng nếu sử dụng nó như một shebang,

#!/usr/bin/env sed -f 
s/a/b/

Kịch bản sẽ không thực thi:

/usr/bin/env: sed -f: No such file or directory

Tôi tin rằng nó liên quan đến -f. Nhưng làm thế nào để giải quyết điều này?


Câu trả lời:


33

Bạn không thể, có thể, đặt nhiều hơn một đối số trên một #!dòng . Điều đó có nghĩa là chỉ một đường dẫn đầy đủ và một đối số (ví dụ:#!/bin/sed -f hoặc #!/usr/bin/sed -f) hoặc #!/usr/bin/envkhông có đối số cho trình thông dịch.

Một cách giải quyết để có được một tập lệnh di động là sử dụng #!/bin/shvà trình bao bọc shell, chuyển tập lệnh sed làm đối số dòng lệnh. Lưu ý rằng điều này không bị POSIX xử phạt (tập lệnh đa hướng dẫn phải được viết bằng một -eđối số riêng cho mỗi hướng dẫn về tính di động), nhưng nó hoạt động với nhiều triển khai.

#!/bin/sh
exec sed '
s/a/b/
' "$@"

Đối với một kịch bản dài, có thể thuận tiện hơn khi sử dụng một di sản. Một lợi thế của một di sản là bạn không cần trích dẫn các trích dẫn duy nhất bên trong, nếu có. Một nhược điểm lớn là tập lệnh được đưa vào sed trên đầu vào tiêu chuẩn của nó, với hai hậu quả khó chịu. Một số phiên bản của sed yêu cầu -f /dev/stdinthay vì -f -, đó là một vấn đề cho tính di động. Tệ hơn, tập lệnh không thể hoạt động như một bộ lọc, vì đầu vào tiêu chuẩn là tập lệnh và không thể là dữ liệu.

#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF

Nhược điểm của di truyền có thể được khắc phục bằng cách sử dụng hữu ích cat. Vì điều này đặt lại toàn bộ tập lệnh trên dòng lệnh, nó không tuân thủ POSIX, nhưng phần lớn có thể di động trong thực tế.

#!/bin/sh
exec sed "$(cat <<'EOF')" -- "$@"
s/a/b/
EOF

Một cách giải quyết khác là viết một tập lệnh có thể được phân tích cú pháp bằng cả sh và bởi sed. Đây là di động, hợp lý hiệu quả, chỉ là một chút xấu xí.

#! /bin/sh
b ()
{
x
}
i\
f true; then exec sed -f "$0" "$@"; fi
: ()
# sed script starts here
s/a/b/

Giải thích:

  • Theo sh: định nghĩa một hàm được gọi b; nội dung không quan trọng miễn là chức năng được hình thành tốt về mặt cú pháp (đặc biệt, bạn không thể có chức năng trống). Sau đó, nếu đúng (tức là luôn luôn), thực hiện sedtrên kịch bản.
  • Dưới sed: nhánh đến ()nhãn, sau đó một số đầu vào được hình thành tốt. Sau đó, một ilệnh, không có hiệu lực vì nó luôn bị bỏ qua. Cuối cùng, ()nhãn tiếp theo là phần hữu ích của tập lệnh.
  • Đã thử nghiệm dưới GNU sed, BusyBox và OpenBSD. (Bạn có thể thoát khỏi một cái gì đó đơn giản hơn trên GNU sed, nhưng OpenBSD sed rất kén chọn các phần mà nó bỏ qua.)

1
"hoặc #!/usr/bin/envvà không có đối số." không phrased rất tốt. Có lẽ "hoặc #!/usr/bin/env sedvà không có đối số để sed."
cjm

+1 cho "chỉ hơi xấu xí" và tập lệnh ngôn ngữ kép :)
truy xuất

6

Có nhiều cách triển khai không tương thích khác nhau của shebang (#!) Tùy thuộc vào HĐH. Một số đang xây dựng một danh sách đối số đầy đủ, một số đang bảo toàn đường dẫn lệnh và đặt tất cả các đối số còn lại thành một đối số duy nhất, một số đang bỏ qua tất cả các đối số và chỉ truyền đường dẫn lệnh và cuối cùng, một số chuyển toàn bộ chuỗi thành một chuỗi chỉ huy. Bạn dường như là trong trường hợp sau.


2

env đang cố gắng tìm một tập tin có tên "sed -f". Bạn có thể thử "#! / Usr / bin / sed -f" làm dòng shebang của bạn.


2

Kể từ GNU coreutils v8.30 , bạn có thể làm:

#!/usr/bin/env -S sed -f

Tính năng này đã được bổ sung trong một gần đây (2018/04/20) cam kết để env.ctrong GNU coreutils gói, trong đó bổ sung thêm -Shoặc --split-stringtùy chọn.

Từ envtrang người đàn ông:

OPTIONS
-S/--split-string usage in scripts
    The  -S  option allows specifing multiple parameters in a script.
    Running a script named 1.pl containing the following first line:

            #!/usr/bin/env -S perl -w -T

    Will execute perl -w -T 1.pl .

    Without the '-S' parameter the script will likely fail with:

            /usr/bin/env: 'perl -w -T': No such file or directory

    See the full documentation for more details.

Các ví dụ khác có sẵn trong hướng dẫn sử dụng lõi GNU .

Nếu bạn cũng sử dụng -v tùy chọn cho đầu ra dài dòng, bạn có thể thấy chính xác cách envchia chuỗi đối số:

Trong my_sed_script.sed:

#!/usr/bin/env -vS sed -f
s/a/b/

Chấp hành:

$ ./my_sed_script.sed
split -S:  ‘sed -f’
 into:    ‘sed’
     &    ‘-f’
executing: sed
   arg[0]= ‘sed’
   arg[1]= ‘-f’
   arg[2]= ‘./my_sed_script.sed’

Lưu ý: Điều này chỉ áp dụng cho các shebang sử dụng /usr/bin/env, như --split-stringlà một tính năng của GNU env.


1

Câu trả lời này cung cấp đường dẫn đến một giải pháp tao nhã: /programming//a/1655389/642372

  1. read một di truyền vào một biến vỏ.
  2. Truyền biến đó thành một đối số vị trí cho sed.

Mẫu vật:

#!/usr/bin/env bash

read -rd '' SED_SCRIPT <<EOD      
# Your sed script goes here.
s/a/b/
EOD

exec sed "$SED_SCRIPT" "$@"

1

Tôi thích giải pháp của Walker, nhưng nó có thể được cải thiện (đưa câu trả lời này vào một câu trả lời riêng vì các bình luận không chấp nhận văn bản được định dạng sẵn). Điều này có thể không hoạt động trên tất cả các phiên bản Linux hoặc Bash, nhưng trên Ubuntu 17.10, bạn có thể cắt phần này xuống như sau:

#!/usr/bin/env bash
read SED_SCRIPT <<EOD
s/a/b/
EOD
sed "$SED_SCRIPT" "$@"

Bạn có thể loại bỏ evalvà đơn giản hóa readlệnh, nhưng bạn phải loại bỏ nhận xét bên trong di truyền.

Ngoài ra, đối với tất cả mọi thứ bạn từng muốn biết về sed nhưng ngại hỏi, có một hướng dẫn rất hữu ích tại http://www.grymoire.com/unix/sed.html . Điều này cho thấy một giải pháp thậm chí tốt hơn, với chi phí mất /usr/bin/envvà mã hóa cứng đường dẫn đến sed:

#!/bin/sed -f
s/a/b/
s/c/d/

Điều này có thêm lợi thế là hỗ trợ nhiều hơn một s///sự thay thế.

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.