Як я можу обробляти необроблені бінарні дані в баш-трубі?


15

У мене є функція bash, яка приймає файл як параметр, перевіряє, що файл існує, а потім записує все, що відходить stdin, у файл. Наївне рішення прекрасно працює з текстом, але у мене виникають проблеми з довільними двійковими даними.

echo -n '' >| "$file" #Truncate the file
while read lines
do  # Is there a better way to do this? I would like one...
    echo $lines >> "$file"
done

Відповіді:


15

Ваш спосіб - додавання розривів рядків до кожної речі, яку вона пише в просторі того, що роздільник ( $IFS) використовує для розділення прочитаного. Замість того, щоб розбити його на нові рядки, просто візьміть цілу справу і передайте її. Ви можете зменшити весь біт коду вище до цього:

 cat - > $file

Вам не потрібен біт усікання, він буде усічений і записувати на нього весь потік STDIN.

Редагувати: Якщо ви використовуєте zsh, ви можете просто використовувати > $fileзамість кота. Ви переспрямовуєте файл і обрізаєте його, але якщо там щось висить, чекаючи, що щось прийме STDIN, він прочитає в цей момент. Я думаю, ви можете зробити щось подібне з bash, але вам доведеться встановити якийсь спеціальний режим.


Я не міг змусити приклад перенаправлення stdin працювати, але змінивши приклад кота на> | (У мене набір noclobber) працює як шарм. Дякую за те, що я
провів

+1 для версії без котів. Завжди уникайте непотрібних котів;)
rozcietrzewiacz

@rozcietrzewiacz: Правда, хіба що це була думка, і я помилявся. Це може бути не марним використанням кота. Єдине, що ви могли б зробити - це > $file. Це працює лише як перше, що шукає stdin у батьківському скрипті оболонки. В основному весь код Девіда може бути зведений до одного символу, але я думаю, що cat -це більш елегантно і менше проблем, тому що це зрозуміло на виду.
Калеб

Іноді я співаю чотири-п’ять catразом, просто щоб роздратувати фанатиків УУПЦ
Майкл Мрозек

@MichaelMrozek: Іноді я називаю свої файли даних catпросто так, щоб люди, які наполягають на його використанні, обов'язково повинні робити розумову гімнастику, щоб прочитати код. Названі труби - також хороші цілі.
Калеб

7

Щоб читати текстовий файл буквально, не використовуйте звичайний текст read, який обробляє вихід двома способами:

  • readтрактує \як втечу персонажа; використовувати, read -rщоб вимкнути це.
  • readрозбивається на слова на символи в $IFS; встановити IFSпорожню рядок, щоб вимкнути це.

Звичайна ідіома для обробки текстового файлу рядок за рядком - це

while IFS= read -r line; do 

Для пояснення цієї ідіоми див. Чому while IFS= readвикористовується так часто замість IFS=; while read..? .

Щоб писати рядок буквально, не використовуйте просто echo, який обробляє рядок двома способами:

  • На деяких оболонках echoобробляється зворотний косий рядок. (Від bash, це залежить від того, чи встановлений xpg_echoваріант.)
  • Кілька рядків розглядаються як варіанти, наприклад, -nабо -e(точний набір залежить від оболонки).

Портативний спосіб друку рядка буквально за допомогою printf. (Немає кращого способу в bash, якщо ви не знаєте, що ваш вклад не виглядає як варіант echo.) Для друку точного рядка використовуйте першу форму, а другу, якщо ви хочете додати новий рядок.

printf %s "$line"
printf '%s\n' "$line"

Це підходить лише для обробки тексту , оскільки:

  • Більшість оболонок задихаються від нульових символів на вході.
  • Прочитавши останній рядок, ви не можете дізнатися, чи був новий рядок наприкінці чи ні. (У деяких старих оболонок можуть виникнути більші проблеми, якщо введення не закінчується новим рядком.)

Ви не можете обробляти бінарні дані в оболонці, але сучасні версії утиліт для більшості юніків можуть впоратися з довільними даними. Щоб передати весь вхід до виходу, використовуйте cat. Їхати по дотичній echo -n ''- це складний і непереносний спосіб нічого не робити; echo -nбуло б настільки ж добре (або не залежно від оболонки), і :є більш простим і повністю портативним.

: >| "$file"
cat >>"$file"

або, простіше,

cat >|"$file"

У сценарії зазвичай не потрібно використовувати, >|оскільки noclobberвін за замовчуванням вимкнено.


дякую за вказівку xpg_echo, це насправді проблема, яку я мав десь ще в коді і навіть не усвідомлював. Re noclobber, у мене є звичка включати це в моєму башерку.
Девід Сутер

0

Це зробить саме те, що ви хочете:

( while read -r -d '' ; do
    printf %s'\0' "${REPLY}" ;
  done ;

  # When read hits EOF, it returns non-zero which exits the while loop.
  # That data still needs to be output:
  printf %s "${REPLY}"
) >> ${file}

Зверніть увагу на використання пам'яті. Це читає введення з нульовим обмеженням.

Якщо на вході немає \0 нульових байтів, то спочатку bash потрібно буде прочитати весь вміст вхідної пам'яті, а потім вивести її.

Щодо кроку укорочення:

echo -n '' >| "$file" #Truncate the file

набагато простішим і рівноцінним є:

> ${file}   #Truncate the file
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.