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 BEGINcâ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/ RSmỗ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 file1trố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 awktên biến hợp lệ .
Cái gì tạo thành một tên biến hợp lệ trong awkchặ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=foohay =barhay ./foo=barvẫ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.txtcó thể hoặc không, tùy thuộc vào việc awkthự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 txttệ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.txtcó 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.txtvới các awkphiê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 ./*.txtcá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 awkhiể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=valuenhững cái có thể được xử lý bằng cách sửa các ARGVgiá trị (thêm ./tiền tố) trong một BEGINcâ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 awkvà không phải là awkkị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 awkkhắc phục tất cả những vấn đề đó với -Etùy chọn của nó .
Sau đó -E, gawk chỉ mong đợi đường dẫn của awktậ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 ARGVdanh sách đó trong một BEGINcâ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 -Evới một tập lệnh trống ( /dev/null) chỉ để đảm bảo những tập lệnh *.txtsau 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/foovà đườ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 awktí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=yVì 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_filenhư 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ì ./mykhô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.txthoặc =foohoặc ./foo=barlà tất cả OK như rằng .hoặc +không phải là một [_a-zA-Z].
./my=filesẽ đượ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ẽ awktì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/awkthực hiện trên Solaris, nhưng dường như gawkcũng không mawklàm điều đó trên GNU / Linux.
awkcá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 awksẽ đọ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=barhoặc đường dẫn đầy đủ tuy nhiên không hoạt động.
Lưu ý rằng chạy stracetrênawk '1' foo=bar cũng như kiểm tra cat foo=barcho 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=barsẽ 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