Tại sao echo bỏ qua các ký tự trích dẫn của tôi?


24

Các echolệnh được không bao gồm các văn bản hoàn chỉnh mà tôi cung cấp cho nó. Ví dụ: nếu tôi làm:

   $ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

Nó xuất ra:

echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `

Các trích dẫn đơn (' ) tôi có trong lệnh echo của tôi không được bao gồm. Nếu tôi chuyển sang dấu ngoặc kép:

$ echo " echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `  "

echokhông đầu ra gì cả! Tại sao nó lại bỏ qua các trích dẫn đơn trong ví dụ đầu tiên và không đưa ra kết quả nào trong lần thứ hai? Làm thế nào để tôi có được nó để xuất chính xác những gì tôi đã gõ?


3
Không hoàn toàn chắc chắn những gì bạn đang cố gắng để đạt được ở đây. Tại sao bạn lồng một câu tiếng vang trong một câu khác? Hay bạn thực sự đang cố gắng in ra một lệnh (ví dụ hoặc như vậy)?
Andy Smith

Tôi cần chèn đầu ra echo vào một tệp khác

Bạn cần giải thích những gì bạn đang cố gắng làm. Chẳng hạn như "Tôi muốn thêm văn bản sau: '' 'vào một tệp."
MikeyB

2
Tôi đã cố gắng chỉnh sửa nó trong 5 phút nhưng tôi thực sự không biết đầu ra mong muốn là gì. Chỉnh sửa : Ok, tôi nghĩ bây giờ tôi đã đoán được, vì vậy tôi đã thử chỉnh sửa. Nếu đó không phải là những gì bạn dự định hãy cho tôi biết
Michael Mrozek

1
@Michael - Wow - Nếu tôi có thể cung cấp cho bạn một số đại diện cho chỉnh sửa của bạn, tôi sẽ - làm tốt công việc này là một câu hỏi thực sự tuyệt vời!
Randall

Câu trả lời:


33

Shell của bạn đang diễn giải các trích dẫn, cả '"trước khi chúng đến echo. Tôi thường chỉ đặt dấu ngoặc kép xung quanh đối số của mình để lặp lại ngay cả khi chúng không cần thiết; ví dụ:

$ echo "Hello world"
Hello world

Vì vậy, trong ví dụ đầu tiên của bạn, nếu bạn muốn bao gồm các dấu ngoặc kép trong đầu ra của mình, chúng cần phải được thoát:

$ echo \'Hello world\'
'Hello world'

Hoặc chúng cần được sử dụng trong một đối số được trích dẫn (nhưng nó không thể là cùng một loại trích dẫn, hoặc bạn sẽ cần phải thoát nó bằng mọi cách):

$ echo "'Hello world'"
'Hello world'

$ echo '"Hello world"'
"Hello world"

Trong ví dụ thứ hai của bạn, bạn đang chạy thay thế lệnh ở giữa chuỗi:

grep  $ARG  /var/tmp/setfile  | awk {print $2}

Những thứ bắt đầu $cũng được xử lý đặc biệt bởi shell - nó coi chúng là các biến và thay thế chúng bằng các giá trị của chúng. Vì rất có thể cả hai biến đó đều không được đặt trong trình bao của bạn, nên nó thực sự chỉ chạy

grep /var/tmp/setfile | awk {print}

Kể từ khi grep chỉ nhìn thấy một đối số, nó giả định rằng đối số đó là mẫu bạn đang tìm kiếm và vị trí mà nó sẽ đọc dữ liệu từ đó là stdin, vì vậy nó chặn chờ đầu vào. Đó là lý do tại sao lệnh thứ hai của bạn dường như bị treo.

Điều này sẽ không xảy ra nếu bạn trích dẫn một đối số (đó là lý do tại sao ví dụ đầu tiên của bạn gần như hoạt động), vì vậy đây là một cách để có được đầu ra bạn muốn:

echo \'' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '\'

Bạn cũng có thể trích dẫn hai lần, nhưng sau đó bạn sẽ cần thoát $s để shell không giải quyết chúng dưới dạng các biến và backticks để shell không chạy thay thế lệnh ngay lập tức:

echo "' echo PARAM=\`  grep  \$ARG  /var/tmp/setfile  | awk '{print \$2}' \`    '"

7

Tôi sẽ không đi sâu vào chi tiết lý do tại sao những nỗ lực của bạn hành xử theo cách họ làm, bởi vì câu trả lời của Michael Mrozek bao gồm rất tốt điều này. Tóm lại, mọi thứ giữa các dấu ngoặc đơn ( '…') được hiểu theo nghĩa đen (và đặc biệt là 'dấu đầu tiên đánh dấu sự kết thúc của chuỗi ký tự), trong khi `$giữ lại ý nghĩa đặc biệt của chúng giữa "…".

Không có trích dẫn trong một trích dẫn, vì vậy bạn không thể đặt một trích dẫn trong một chuỗi trích dẫn. Tuy nhiên, có một thành ngữ trông giống như nó:

echo 'foo'\''bar'

Bản in này foo'bar, bởi vì đối số echođược tạo từ chuỗi ba ký tự được trích dẫn đơn foo, được nối với ký tự đơn '(thu được bằng cách bảo vệ ký tự 'khỏi ý nghĩa đặc biệt của nó thông qua trước đó \), được nối với chuỗi ba ký tự được trích dẫn bar. Vì vậy, mặc dù đó không hoàn toàn là những gì xảy ra đằng sau hậu trường, bạn có thể nghĩ về'\'' một cách để bao gồm một trích dẫn bên trong một chuỗi trích dẫn.

Nếu bạn muốn in các chuỗi đa dòng phức tạp, một công cụ tốt hơn là tài liệu ở đây . Một tài liệu ở đây bao gồm hai ký tự <<theo sau là một điểm đánh dấu <<EOF, sau đó là một số dòng văn bản, sau đó là dấu kết thúc một mình trên dòng của nó. Nếu điểm đánh dấu được trích dẫn theo bất kỳ cách nào ( 'EOF'hoặc "EOF"hoặc \EOF'E "" OF' hoặc '), thì văn bản được hiểu theo nghĩa đen (như bên trong các trích dẫn đơn lẻ, ngoại trừ đó 'là một ký tự bình thường). Nếu điểm đánh dấu hoàn toàn không được trích dẫn, thì văn bản được diễn giải như trong một chuỗi trích dẫn kép, với $\`trạng thái đặc biệt của chúng (nhưng các "dòng mới được hiểu theo nghĩa đen).

cat <<'EOF'
echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `
EOF

1

Được rồi, điều này sẽ làm việc: -

echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

Kiểm tra nó ở đây: -

[asmith@yagi ~]$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '
 echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' ` 

0

Gợi ý sử dụng tài liệu ở đây của Gilles thực sự rất hay và thậm chí hoạt động trong các ngôn ngữ kịch bản như Perl. Như một ví dụ cụ thể dựa trên câu trả lời của anh ấy về vấn đề của OP,

use strict;
my $cmd = q#cat <<'EOF' # . "\n" .
          q#echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `# . "\n" .
          q#EOF# ;
open (my $OUTPUT, "$cmd |");
print $_ while <$OUTPUT>;
close $OUTPUT;

Cấp, ví dụ này là một chút giả định, nhưng tôi đã thấy nó là một kỹ thuật hữu ích để, nói, gửi các câu lệnh SQL đến psql(thay vìcat ).

(Lưu ý rằng mọi ký tự không phải chữ và số có thể được sử dụng thay cho #cấu trúc trích dẫn chung ở trên, nhưng hàm băm dường như nổi bật trong ví dụ này và không được coi là một nhận xét.)


-1

Hãy nhận lệnh của bạn

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

và trước tiên chia nó thành các từ, sử dụng khoảng trắng không trích dẫn làm dấu phân cách:

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“$2}' `    '”

Tiếp theo, chúng tôi mở rộng tham số: mở rộng $…nhưng không bên trong '…'. Vì $2là trống (trừ khi bạn đặt nó qua ví dụ set -- …), nó sẽ được thay thế bằng một chuỗi trống.

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“}' `    '”

Tiếp theo, làm trích dẫn loại bỏ.

Arg[1]=“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print”
Arg[2]=“} `    ”

Các đối số này sau đó được chuyển sang echo, sẽ in chúng lần lượt từng cái, cách nhau bởi một khoảng trắng.

“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `    ”

Tôi hy vọng rằng hướng dẫn từng bước này làm cho hành vi được quan sát rõ ràng hơn. Để tránh sự cố, thoát khỏi dấu ngoặc đơn như thế này:

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

Điều này sẽ kết thúc chuỗi trích dẫn đơn, sau đó thêm một trích dẫn (thoát) vào từ đó, sau đó bắt đầu một chuỗi trích dẫn đơn mới, tất cả mà không phá vỡ từ.

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.