Trong Bash, khi chỉ định các đối số dòng lệnh cho một lệnh, những ký tự nào được yêu cầu để thoát?
Họ đang giới hạn ở những metacharacters của Bash: không gian, tab,
|
, &
, ;
, (
, )
, <
, và >
?
Trong Bash, khi chỉ định các đối số dòng lệnh cho một lệnh, những ký tự nào được yêu cầu để thoát?
Họ đang giới hạn ở những metacharacters của Bash: không gian, tab,
|
, &
, ;
, (
, )
, <
, và >
?
Câu trả lời:
Các ký tự sau có ý nghĩa đặc biệt đối với chính vỏ trong một số ngữ cảnh và có thể cần phải thoát trong các đối số:
`
Backtick ( Dấu trọng âm U + 0060)~
Dấu ngã (U + 007E)!
Dấu chấm than (U + 0021)#
Hash (Ký hiệu số U + 0023)$
Ký hiệu đô la (U + 0024)&
Dấu và (U + 0026)*
Dấu hoa thị (U + 002A)(
Dấu ngoặc trái (U + 0028))
Dấu ngoặc phải (U + 0029)
( ⇥
) Tab (U + 0009){
Nẹp trái (U + 007B Khung xoăn trái)[
Khung vuông bên trái (U + 005B)|
Thanh dọc (Đường thẳng đứng U + 007C)\
Dấu gạch chéo ngược (Solid + Reverse Reverse U + 005C);
Dấu chấm phẩy (U + 003B)'
Trích dẫn đơn / Dấu nháy đơn (U + 0027)"
Báo giá kép (U + 0022)↩
Dòng mới (U + 000A)<
Ít hơn (U + 003C)>
Lớn hơn (U + 003E)?
Dấu hỏi (U + 003F)
Không gian (U + 0020) 1Một số trong những nhân vật đó được sử dụng cho nhiều thứ hơn và ở nhiều nơi hơn so với cái tôi liên kết.
Có một vài trường hợp góc được tùy chọn rõ ràng:
!
có thể bị vô hiệu hóa với set +H
, đó là mặc định trong shell không tương tác.{
có thể bị vô hiệu hóa với set +B
.*
và ?
có thể bị vô hiệu hóa với set -f
hoặcset -o noglob
.=
Dấu bằng (U + 003D) cũng cần được thoát nếu set -k
hoặcset -o keyword
được bật.Thoát một dòng mới yêu cầu trích dẫn - dấu gạch chéo ngược sẽ không thực hiện công việc. Bất kỳ nhân vật nào khác được liệt kê trong IFS sẽ cần xử lý tương tự. Bạn không cần phải thoát khỏi ]
hay }
, nhưng bạn làm cần phải thoát khỏi )
bởi vì nó là một nhà điều hành.
Một số trong những nhân vật này có giới hạn chặt chẽ hơn khi họ thực sự cần trốn thoát hơn những người khác. Ví dụ, a#b
là ok, nhưng a #b
là một nhận xét, trong khi >
sẽ cần thoát trong cả hai bối cảnh. Dù sao đi nữa, việc thoát khỏi tất cả chúng một cách bảo thủ, và việc ghi nhớ những nét riêng biệt sẽ không dễ dàng gì.
Nếu tên lệnh của bạn tự nó là một từ khóa shell ( if
, for
, do
) sau đó bạn sẽ cần phải thoát khỏi hoặc trích dẫn nó quá. Điều thú vị duy nhất trong số đó là in
bởi vì nó không rõ ràng rằng nó luôn là một từ khóa. Bạn không cần phải làm điều đó cho các từ khóa được sử dụng trong các đối số, chỉ khi bạn (dại dột!) Đặt tên một lệnh theo một trong số chúng. Toán tử Shell ( (
, &
v.v.) luôn cần trích dẫn mọi lúc mọi nơi.
1 Stéphane đã lưu ý rằng bất kỳ ký tự trống một byte nào khác từ miền địa phương của bạn cũng cần thoát. Trong hầu hết các địa phương phổ biến, hợp lý, ít nhất là các địa điểm dựa trên C hoặc UTF-8, đó chỉ là các ký tự khoảng trắng ở trên. Trong một số địa điểm ISO-8859-1, không gian không ngắt U + 00A0 được coi là trống, bao gồm Solaris, BSD và OS X (tôi nghĩ không chính xác). Nếu bạn đang làm việc với một địa điểm không xác định tùy ý, nó có thể bao gồm bất cứ thứ gì, kể cả thư, thật may mắn.
Có thể hiểu được, một byte đơn được coi là trống có thể xuất hiện trong một ký tự nhiều byte không trống và bạn không có cách nào để thoát khỏi điều đó ngoài việc đặt toàn bộ nội dung trong dấu ngoặc kép. Đây không phải là mối quan tâm về mặt lý thuyết: trong miền địa phương ISO-8859-1 từ phía trên, A0
byte được coi là khoảng trống có thể xuất hiện trong các ký tự đa bào như UTF-8 được mã hóa "à" ( C3 A0
). Để xử lý các ký tự đó một cách an toàn, bạn sẽ cần trích dẫn chúng "à"
. Hành vi này phụ thuộc vào cấu hình ngôn ngữ trong môi trường chạy tập lệnh chứ không phải nơi bạn viết nó.
Tôi nghĩ rằng hành vi này bị phá vỡ theo nhiều cách, nhưng chúng ta phải chơi theo cách chúng ta xử lý. Nếu bạn đang làm việc với bất kỳ bộ ký tự đa nhân không tự đồng bộ hóa nào, điều an toàn nhất sẽ là trích dẫn mọi thứ. Nếu bạn đang ở UTF-8 hoặc C, bạn sẽ an toàn (hiện tại).
!
khi mở rộng lịch sử csh, thường không có trong tập lệnh. [ ! -f a ]
hoặc find . ! -name...
là tốt Điều đó được bao phủ bởi phần giới hạn chặt chẽ hơn của bạn nhưng có thể đáng được đề cập rõ ràng.
hash[foo"]"]=
, ${var-foo"}"}
, [[ "!" = b ]]
, [[ a = "]]" ]]
, các nhà khai thác regexp cho [[ x =~ ".+[" ]]
. Từ khóa khác hơn {
( if
, while
, for
...) sẽ cần phải được trích dẫn vì vậy họ đang không được công nhận như vậy ...
]
), vì vậy tôi không liệt kê chúng. Tôi không nghĩ rằng bất kỳ từ khóa cần trích dẫn ở vị trí đối số.
Trong GNU Parallel, điều này được kiểm tra và sử dụng rộng rãi:
$a =~ s/[\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]/\\$&/go;
# quote newline as '\n'
$a =~ s/[\n]/'\n'/go;
Nó được thử nghiệm trên bash
, dash
, ash
, ksh
, zsh
, và fish
. Một số ký tự không cần trích dẫn trong một số (phiên bản) của vỏ, nhưng các ký tự trên hoạt động trong tất cả các vỏ được thử nghiệm.
Nếu bạn chỉ đơn giản muốn một chuỗi trích dẫn, bạn có thể chuyển nó thành parallel --shellquote
:
printf "&*\t*!" | parallel --shellquote
Đối với giải pháp thoát nhẹ trong Perl, tôi tuân theo nguyên tắc trích dẫn đơn. Một chuỗi Bash trong các trích dẫn đơn có thể có bất kỳ ký tự nào, ngoại trừ chính trích dẫn đó.
Mã của tôi:
my $bash_reserved_characters_re = qr([ !"#$&'()*;<>?\[\\`{|~\t\n]);
while(<>) {
if (/$bash_reserved_characters_re/) {
my $quoted = s/'/'"'"'/gr;
print "'$quoted'";
} else {
print $_;
}
}
Ví dụ chạy 1:
$ echo -n "abc" | perl escape_bash_special_chars.pl
abc
Ví dụ chạy 2:
echo "abc" | perl escape_bash_special_chars.pl
'abc
'
Ví dụ chạy 3:
echo -n 'ab^c' | perl escape_bash_special_chars.pl
ab^c
Ví dụ chạy 4:
echo -n 'ab~c' | perl escape_bash_special_chars.pl
'ab~c'
Ví dụ chạy 5:
echo -n "ab'c" | perl escape_bash_special_chars.pl
'ab'"'"'c'
echo 'ab'"'"'c'
ab'c