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
вибором.
Після цього -E
gawk очікує лише шлях 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