awk 'processing_script_here' my=file.txt
здається, зупиняється і чекає нескінченно ...
Що тут відбувається і як змусити це працювати?
awk 'processing_script_here' my=file.txt
здається, зупиняється і чекає нескінченно ...
Що тут відбувається і як змусити це працювати?
Відповіді:
Як каже Кріс , аргументи форми variablename=anythingтрактуються як призначення змінних (які виконуються під час обробки аргументів на відміну від (новіших) -v var=value, які виконуються перед BEGINоператорами) замість імен вхідних файлів.
Це може бути корисно в таких речах, як:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Де ви можете вказати інший FS/ RSна файл. Він також часто використовується в:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Яка безпечніша версія:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(яка не працює, якщо file1порожня)
Але це стає на шляху, коли у вас є файли, ім'я яких містить =символи.
Тепер це лише проблема, коли те, що залишилося від першого, =- це дійсне awkім'я змінної.
Те, що являє собою дійсне ім'я змінної в awk, суворіше ніж у sh.
POSIX вимагає, щоб це було щось на зразок:
[_a-zA-Z][_a-zA-Z0-9]*
Тільки символи портативного набору символів. Однак, /usr/xpg4/bin/awkпринаймні, Solaris 11 в цьому плані не відповідає, і дозволяє будь-які алфавітні символи в мові змінних імен, а не лише a-zA-Z.
Отже, аргумент на зразок x+y=fooабо =barабо ./foo=barдосі трактується як ім'я вхідного файлу, а не присвоєння, оскільки те, що залишилося від першого =, не є дійсним ім'ям змінної. Аргумент на кшталт Stéphane=Chazelas.txtможе бути, а може і не, залежно від awkреалізації та місцевості.
Ось чому з awk рекомендується використовувати:
awk '...' ./*.txt
замість
awk '...' *.txt
наприклад, щоб уникнути проблеми, якщо ви не можете гарантувати, що ім'я txtфайлів не міститиме =символів.
Також майте на увазі, що такий аргумент -vfoo=bar.txtможе трактуватися як варіант, якщо ви використовуєте:
awk -f file.awk -vfoo=bar.txt
(Також відноситься і до awk '{code}' -vfoo=bar.txtз awkвід версії BusyBox до 1.28.0, см відповідного повідомлення про помилку ).
Знову ж таки, використання ./*.txtнавколо цього робіт (використання ./префікса також допомагає з файлом, -який інакше замість цього awkозначає як стандартний ввід ).
Це також чому
#! /usr/bin/awk -f
shebangs насправді не працюють. У той час як var=valueті , можна обійти шляхом фіксації на ARGVзначення (додати ./префікс) в BEGINзаяві:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
Це не допоможе з варіантами, оскільки ті, які вони бачать, awkа не awkсценарій.
Один з потенційних косметичних проблем із використанням цього ./префікса - це він закінчується FILENAME, але ви завжди можете substr(FILENAME, 3)його знімати, якщо цього не хочете.
Реалізація GNU awkвиправляє всі ці проблеми з її -Eвибором.
Після цього -Egawk очікує лише шлях awkсценарію (де -все ще означає stdin), а потім список тільки шляхів до вхідних файлів (а там навіть -не обробляється спеціально).
Він спеціально розроблений для:
#! /usr/bin/gawk -E
shebangs, де список аргументів завжди є вхідними файлами (зауважте, що ви все ще можете безкоштовно редагувати цей ARGVсписок у BEGINвиписці).
Ви також можете використовувати його як:
gawk -e '...awk code here...' -E /dev/null *.txt
Ми використовуємо -Eпорожній скрипт ( /dev/null) просто для того, щоб переконатися, що *.txtпісля цього завжди трактуються як вхідні файли, навіть якщо вони містять =символи.
../foo, /path/to/fooта шляхи, що мають інше кодування) - у цьому випадку substr(FILENAME,3)цього буде недостатньо, або це скрипт з одним знімком, де користувач в основному знає, що таке імена файлів - в такому випадку він / він, мабуть, не повинен заважати жодному з них, що містить =будь-яке ;-)
./це проблема, але це може бути небажаним за певних умов, наприклад, коли ім'я файлу повинно бути включене у висновок, у цьому випадку ./має бути зайвим і непотрібним, тож ви Мені потрібно якось позбутися цього. Ось хоча б один приклад . Що стосується того, щоб користувач знав, що таке імена файлів - добре, в цьому випадку ми також знаємо, що таке ім'я файлу, але =все-таки перешкоджає правильній обробці. Так що провідні можуть -заважати.
./префікс, щоб обійти цю awk(неправильну) функцію, але потім ви отримаєте той, що ./на виході, який ви можете зняти. Подивіться, як перевірити, чи містить перший рядок файлу певний рядок? як приклад.
./а й глобальний (абсолютний шлях) /.
У більшості версій awk аргументами після виконання програми є або:
x=yОскільки ваше ім'я інтерпретується як випадок №2, awk все ще чекає, що прочитати на stdin (оскільки він не сприймає, що було передано якесь ім’я файлу).
Портативно ця поведінка задокументована в POSIX :
Будь-який із наступних типів аргументів може бути змішаний:
- file: ім'я файлу, який містить вхід для зчитування, який співпадає з набором шаблонів у програмі. Якщо жодні операнди файлів не вказані, або якщо файловий операнд "-", використовується стандартний ввід.
- призначення: операнд, який починається з символу підкреслення або алфавіту з переносного набору символів (див. таблицю в томі базових визначень IEEE Std 1003.1-2001, розділ 6.1, Портативний набір символів), після чого послідовність підкреслення, цифри, а алфавіти з переносного набору символів, а потім символ "=", повинні вказувати змінне призначення, а не ім'я шляху.
Як таке, портативно, у вас є кілька варіантів (№1, мабуть, найменш нав'язливий):
awk ... ./my=file, який переходить до цього, оскільки .це не "підкреслювальний або алфавітний символ з набору переносних символів".awk ... < my=file. Однак це не добре працює з кількома файлами.ln my=file my_file, а потім використовувати my_fileяк звичайне. Копіювання не буде виконуватися, і обидва файли будуть підкріплені тими ж даними та метаданими inode. Після його використання безпечно видалити створене посилання, оскільки кількість посилань на inode все ще буде більше 0../my=file працює? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory). Це повинно бути портативним, оскільки ./myце неправдиве ім'я змінної, тому його не слід аналізувати таким чином.
=передує підкреслювальний або алфавітний символ із портативного набору символів (див. Таблицю в томі базових визначень IEEE Std 1003.1-2001, розділ 6.1, Портативний набір символів), з наступною послідовністю підкреслення, цифр та алфавітів з набору переносних символів . тому шлях до файлу , як ++foo=bar.txtі =fooчи ./foo=barвсе в порядку , як .і +не є [_a-zA-Z].
./my=fileбуде передано дослівно.
awk '{print $1,$2}' /etc/passwd. Справа в тому, що відкриття файлу оболонки на відміну від awk не має жодних значень щодо того, чи робить його шукаючим чи ні. Насправді, у цьому випадку awk '{exit}' < /etc/passwd, ви б очікували, awkщо звернетесь до кінця першого запису, exitщоб переконатися, що він залишив позицію в межах stdin там. POSIX вимагає цього. /usr/xpg4/bin/awkробить це на Solaris, але ні, gawkні mawkздається, це робити в GNU / Linux.
awkтаким чином.
Щоб процитувати документацію gawk (додано наголос):
Будь-які додаткові аргументи в командному рядку зазвичай трактуються як вхідні файли, які обробляються у визначеному порядку. Однак аргумент, що має форму var = value, присвоює значення значенню змінної var - він взагалі не вказує файл.
Чому команда зупиняється і чекає? Оскільки у формі awk 'processing_script_here' my=file.txt немає файлу, визначеного вищевказаним визначенням - my=file.txtінтерпретується як присвоєння змінної, і якщо немає визначеного файлу awk, читатиме stdin (також видно, з straceякого видно, що awk у такій команді чекає на read(0,'...)syscall.
Це також описано в специфікації AWK POSIX см операнди розділ і Призначення частина цього)
Змінне призначення очевидно, awk '{print foo}' foo=bar /etc/passwdщо значення fooдрукується для кожного рядка в / etc / passwd. Вказання ./foo=barабо повний шлях проте працює.
Зверніть увагу , що працює straceна awk '1' foo=bar, а також перевірки з cat foo=barпоказує , що це AWK-специфічна проблема, і execve робить шоу імені файлу в якості аргументу передається, тому снаряди не мають нічого спільного зі змінним окр завдань в цьому випадку.
Крім того, зауважте, що awk '...script...' foo=barце не спричинить створення змінних оточення оболонками, оскільки призначення змінних оточуючих середовищ має передувати команді, що набуває чинності. Див. POSIX Shell Grammar Rules , пункт № 7. Додатково це можна перевірити за допомогоюawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd