awk 'processing_script_here' my=file.txt
dường như dừng lại và chờ đợi vô thời hạn ...
Chuyện gì đang xảy ra ở đây và làm thế nào để tôi làm cho nó hoạt động?
awk 'processing_script_here' my=file.txt
dường như dừng lại và chờ đợi vô thời hạn ...
Chuyện gì đang xảy ra ở đây và làm thế nào để tôi làm cho nó hoạt động?
Câu trả lời:
Như Chris nói , các đối số của biểu mẫu variablename=anything
được coi là phép gán biến (được thực hiện tại thời điểm các đối số được xử lý trái ngược với các đối số (mới hơn) -v var=value
được thực hiện trước các BEGIN
câu lệnh) thay vì tên tệp đầu vào.
Điều đó có thể hữu ích trong những việc như:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Nơi bạn có thể chỉ định một tập tin khác nhau FS
/ RS
mỗi. Nó cũng thường được sử dụng trong:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Phiên bản nào an toàn hơn:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(không hoạt động nếu file1
trống)
Nhưng điều đó cản trở khi bạn có các tệp có tên chứa các =
ký tự.
Bây giờ, đó chỉ là một vấn đề khi cái còn lại của cái đầu tiên =
là một awk
tên biến hợp lệ .
Cái gì tạo thành một tên biến hợp lệ trong awk
chặt chẽ hơn trong sh
.
POSIX yêu cầu nó phải giống như:
[_a-zA-Z][_a-zA-Z0-9]*
Chỉ với các ký tự của bộ ký tự di động. Tuy nhiên, /usr/xpg4/bin/awk
ít nhất Solaris 11 không tuân thủ về vấn đề đó và cho phép bất kỳ ký tự chữ cái nào trong miền địa phương trong tên biến, không chỉ a-zA-Z.
Vì vậy, một cuộc tranh cãi như x+y=foo
hay =bar
hay ./foo=bar
vẫn đối xử như một tên tập tin đầu vào và không phải là một nhiệm vụ như những gì còn lại của người đầu tiên =
không phải là một tên biến hợp lệ. Một đối số như Stéphane=Chazelas.txt
có thể hoặc không, tùy thuộc vào việc awk
thực hiện và ngôn ngữ.
Đó là lý do tại sao với awk, nên sử dụng:
awk '...' ./*.txt
thay vì
awk '...' *.txt
ví dụ để tránh sự cố nếu bạn không thể đảm bảo tên của txt
tệp sẽ không chứa =
ký tự.
Ngoài ra, hãy cẩn thận rằng một đối số như -vfoo=bar.txt
có thể được coi là một tùy chọn nếu bạn sử dụng:
awk -f file.awk -vfoo=bar.txt
(cũng áp dụng awk '{code}' -vfoo=bar.txt
với các awk
phiên bản busybox trước 1.28.0, xem báo cáo lỗi tương ứng ).
Một lần nữa, sử dụng ./*.txt
các công trình xung quanh đó (sử dụng một ./
tiền tố cũng giúp với một tập tin gọi -
mà nếu không awk
hiểu như nghĩa đầu vào tiêu chuẩn thay vì).
Đó cũng là lý do tại sao
#! /usr/bin/awk -f
shebangs không thực sự làm việc. Trong khi var=value
những cái có thể được xử lý bằng cách sửa các ARGV
giá trị (thêm ./
tiền tố) trong một BEGIN
câu lệnh:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
Điều đó sẽ không giúp với các tùy chọn như những cái được nhìn thấy awk
và không phải là awk
kịch bản.
Một vấn đề thẩm mỹ tiềm năng khi sử dụng ./
tiền tố đó là nó kết thúc FILENAME
, nhưng bạn luôn có thể sử dụng substr(FILENAME, 3)
để loại bỏ nó nếu bạn không muốn.
Việc triển khai GNU awk
khắc phục tất cả những vấn đề đó với -E
tùy chọn của nó .
Sau đó -E
, gawk chỉ mong đợi đường dẫn của awk
tập lệnh (trong đó -
vẫn có nghĩa là stdin) và sau đó là danh sách các đường dẫn tệp đầu vào (và ở đó, thậm chí không -
được xử lý đặc biệt).
Nó được thiết kế đặc biệt cho:
#! /usr/bin/gawk -E
shebang nơi danh sách các đối số luôn là các tệp đầu vào (lưu ý rằng bạn vẫn có thể tự do chỉnh sửa ARGV
danh sách đó trong một BEGIN
câu lệnh).
Bạn cũng có thể sử dụng nó như:
gawk -e '...awk code here...' -E /dev/null *.txt
Chúng tôi sử dụng -E
với một tập lệnh trống ( /dev/null
) chỉ để đảm bảo những tập lệnh *.txt
sau luôn được coi là tập tin đầu vào, ngay cả khi chúng có chứa các =
ký tự.
../foo
, /path/to/foo
và đường dẫn mà đang ở trong một mã hóa khác nhau) - trong trường hợp này substr(FILENAME,3)
sẽ không đủ, hoặc nó một kịch bản một cảnh quay mà người dùng về cơ bản biết tên tập tin là gì - trong trường hợp đó có lẽ anh ta không nên bận tâm với bất kỳ ai trong số họ có chứa =
;-)
./
vấn đề, nhưng nó có thể không mong muốn trong một số điều kiện nhất định, chẳng hạn như các trường hợp tên tệp phải được đưa vào đầu ra, trong trường hợp ./
đó là dư thừa và không cần thiết, vì vậy bạn Sẽ cần phải thoát khỏi nó bằng cách nào đó. Dưới đây là ít nhất một ví dụ . Đối với người dùng biết tên tệp là gì - tốt, trong trường hợp này, chúng tôi cũng biết tên tệp là gì, nhưng =
vẫn có cách xử lý đúng. Vì vậy, có thể dẫn đầu -
có được trong cách.
./
tiền tố để làm việc xung quanh awk
tính năng (mis) đó nhưng sau đó bạn kết thúc với một ./
đầu ra mà bạn có thể muốn loại bỏ. Xem cách kiểm tra xem dòng đầu tiên của tệp có chứa một chuỗi cụ thể không? làm ví dụ
./
mà còn là toàn cục (đường dẫn tuyệt đối) /
làm cho awk diễn giải đối số dưới dạng tệp.
Trong hầu hết các phiên bản của awk, các đối số sau khi chương trình thực thi là:
x=y
Vì tên tệp của bạn đang được hiểu là trường hợp # 2, awk vẫn đang chờ một cái gì đó để đọc trên stdin (vì nó không nhận thấy rằng đã có bất kỳ tên tệp nào được thông qua).
Có thể, hành vi này được ghi lại trong POSIX :
Một trong hai loại đối số sau đây có thể được trộn lẫn với nhau:
- tệp: Tên đường dẫn của tệp chứa đầu vào cần đọc, được khớp với nhóm mẫu trong chương trình. Nếu không có toán hạng tệp nào được chỉ định hoặc nếu toán hạng tệp là '-', thì đầu vào tiêu chuẩn sẽ được sử dụng.
- gán: Một toán hạng bắt đầu bằng ký tự gạch dưới hoặc chữ cái từ bộ ký tự di động (xem bảng trong tập Định nghĩa cơ sở của IEEE Std 1003.1-2001, Phần 6.1, Bộ ký tự di động), theo sau là một chuỗi các dấu gạch dưới, chữ số, và bảng chữ cái từ bộ ký tự di động, theo sau là ký tự '=', sẽ chỉ định một phép gán biến thay vì tên đường dẫn.
Như vậy, có thể, bạn có một vài lựa chọn (# 1 có thể là ít xâm phạm nhất):
awk ... ./my=file
, điều này vượt qua điều này vì .
không phải là "ký tự gạch dưới hoặc chữ cái trong bộ ký tự di động".awk ... < my=file
. Tuy nhiên, điều này không hoạt động tốt với nhiều tập tin.ln my=file my_file
, và sau đó sử dụng my_file
như bình thường. Không sao chép sẽ được thực hiện và cả hai tệp sẽ được hỗ trợ bởi cùng một dữ liệu và siêu dữ liệu inode. Sau khi sử dụng, an toàn để xóa liên kết được tạo vì số lượng tham chiếu đến nút vẫn sẽ lớn hơn 0../my=file
hoạt động? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Đây phải là di động vì ./my
không phải là tên biến hợp lệ, vì vậy không nên phân tích cú pháp theo cách đó.
=
đứng trước ký tự gạch dưới hoặc chữ cái từ bộ ký tự di động (xem bảng trong tập Định nghĩa cơ sở của IEEE Std 1003.1-2001, Phần 6.1, Bộ ký tự di động), theo sau là một chuỗi các dấu gạch dưới, chữ số và bảng chữ cái từ bộ ký tự di động . do đó, một đường dẫn tập tin như ++foo=bar.txt
hoặc =foo
hoặc ./foo=bar
là tất cả OK như rằng .
hoặc +
không phải là một [_a-zA-Z]
.
./my=file
sẽ được chuyển qua nguyên văn.
awk '{print $1,$2}' /etc/passwd
. Vấn đề là việc shell mở tệp trái ngược với awk sẽ không tạo ra bất kỳ sự khác biệt nào về việc liệu nó có thể tìm kiếm được hay không. Trên thực tế, trong awk '{exit}' < /etc/passwd
, bạn sẽ awk
tìm cách quay lại phần cuối của bản ghi đầu tiên exit
để đảm bảo rằng nó rời khỏi vị trí trong stdin ở đó. POSIX yêu cầu điều đó. /usr/xpg4/bin/awk
thực hiện trên Solaris, nhưng dường như gawk
cũng không mawk
làm điều đó trên GNU / Linux.
awk
cách đó.
Để trích dẫn tài liệu gawk (lưu ý nhấn mạnh thêm):
Bất kỳ đối số bổ sung nào trên dòng lệnh thường được coi là tệp đầu vào được xử lý theo thứ tự được chỉ định. Tuy nhiên, một đối số có dạng var = value, gán giá trị giá trị cho biến var, nó không chỉ định một tệp nào cả.
Tại sao lệnh dừng lại và chờ đợi? Bởi vì trong biểu mẫu awk 'processing_script_here' my=file.txt
không có tệp nào được định nghĩa theo định nghĩa trên - my=file.txt
được hiểu là phép gán biến và nếu không có tệp nào được xác định awk
sẽ đọc stdin (cũng hiển nhiên từ strace
đó cho thấy awk trong lệnh đó đang chờ trên read(0,'...)
tòa nhà.
Điều này cũng được ghi lại trong thông số kỹ thuật POSIX awk , xem phần OPERANDS và phần bài tập trong đó)
Phân công biến là hiển nhiên trong awk '{print foo}' foo=bar /etc/passwd
giá trị đó foo
được in cho mỗi dòng trong / etc / passwd. Chỉ định ./foo=bar
hoặc đường dẫn đầy đủ tuy nhiên không hoạt động.
Lưu ý rằng chạy strace
trênawk '1' foo=bar
cũng như kiểm tra cat foo=bar
cho thấy đây là vấn đề cụ thể của awk và execve không hiển thị tên tệp là đối số được truyền, vì vậy shell không liên quan gì đến các phép gán biến env trong trường hợp này.
Ngoài ra, xin lưu ý rằng awk '...script...' foo=bar
sẽ không gây ra việc tạo biến môi trường bằng shell, vì các phép gán biến môi trường phải có trước một lệnh để có hiệu lực. Xem Quy tắc ngữ pháp POSIX Shell , điểm số 7. Ngoài ra, điều này có thể được xác minh thông quaawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd