Как избежать подстановки команд bash для удаления символа новой строки?

Чтобы ускорить выполнение некоторого bash-скрипта, я хотел бы сохранить результат команды в переменной, используя подстановку команд, но подстановка команд заменяет 0x0A символ новой строки через пробел. Например:

a=`df -H`

или же

a=$( df -H )

Когда я хочу обработать дальше $aсимволы новой строки заменяются пробелом, и все строки теперь находятся на одной строке, что гораздо сложнее:

echo $a

Какими бы простыми приемами можно было избежать удаления символа новой строки при подстановке команды?

2 ответа

Решение

Не завершающие переводы строки не удаляются

Новые строки, которые вы ищете, есть, вы просто не видите их, потому что вы используете echo без кавычек переменной.

Проверка:

$ a=$( df -H )
$ echo $a
Filesystem Size Used Avail Use% Mounted on /dev/sda3 276G 50G 213G 19% / udev 2.1G 4.1k 2.1G 1% /dev tmpfs 832M 820k 832M 1% /run none 5.3M 0 5.3M 0% /run/lock none 2.1G 320k 2.1G 1% /run/shm
$ echo "$a"
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda3       276G   50G  213G  19% /
udev            2.1G  4.1k  2.1G   1% /dev
tmpfs           832M  820k  832M   1% /run
none            5.3M     0  5.3M   0% /run/lock
none            2.1G  320k  2.1G   1% /run/shm
$ 

Конечные переводы строки удаляются

Как правильно указал @user4815162342, хотя новые строки в выходных данных не удаляются,конечные новые строки удаляются с подстановкой команд. Смотрите эксперимент ниже:

$ a=$'test\n\n'
$ echo "$a"
test


$ b=$(echo "$a")
$ echo "$b"
test
$

В большинстве случаев это не имеет значения, потому что echoдобавит удаленный перевод строки (если он не вызывается с-nвариант), но есть некоторые крайние случаи, когда в выводе программы есть более одного завершающего символа новой строки, и они важны по какой-то причине.

обходные

1. Добавить пустышку

В этих случаях, как упоминалось в@Scrutinizer, вы можете использовать следующий обходной путь:

$ a=$(printf 'test\n\n'; printf x); a=${a%x}
$ echo "$a"
test


$ 

Пояснение: Персонаж xдобавляется к выводу (используяprintf x), после перевода строки. Поскольку переводы строк больше не завершаются, они не удаляются подстановкой команд. Следующим шагом является удаление xмы добавили, используя%оператор в${a%x}, Теперь у нас есть оригинальный вывод со всеми присутствующими символами новой строки!!!

2. Чтение с использованием процесса замены

Вместо использования подстановки команд для назначения выходных данных программы переменной, мы можем вместо этого использовать подстановку процессов для передачи выходных данных программыreadвстроенная команда (кредит@ormaaj). Процесс подстановки сохраняет все новые строки. Чтение вывода в переменную немного сложно, но вы можете сделать это так:

$ IFS= read -rd '' var < <( printf 'test\n\n' ) 
$ echo "$var"
test


$ 

Объяснение:

  • Мы устанавливаем внутренний разделитель полей для команды чтения на ноль, с IFS=, Иначеreadне назначит весь выводvar, но только первый токен.
  • Мы призываемread с вариантами -rd '', r для предотвращения обратной косой черты, чтобы действовать как специальный символ, и с d '' установите разделитель на ничего, чтобы read читал весь вывод, а не только первую строку.

3. Читайте из трубы

Вместо того чтобы использовать подстановку команды или процесса для присвоения выходных данных программы переменной, мы можем вместо этого направить выходные данные программы в read команда (кредит @ormaaj). Трубопровод также сохраняет все новые строки. Обратите внимание, что на этот раз мы установили lastpipe необязательное поведение оболочки, используя shopt встроенный Это необходимо для того, чтобы read Команда выполняется в текущей среде оболочки. В противном случае переменная будет назначена в подоболочке и не будет доступна из остальной части сценария.

$ cat test.sh 
#!/bin/bash
shopt -s lastpipe
printf "test\n\n" | IFS= read -rd '' var
echo "$var"
$ ./test.sh 
test


$

Еще один "изящный прием" - использовать символ возврата каретки, который предотвращает удаление новой строки, но ничего не добавляет к выводу:

$ my_func_1 () {
>     echo "This newline is squashed"
> }
$ my_func_2 () {
>     echo "This newline is not squashed"
>     echo -n $'\r'
> }
$ echo -n "$(my_func_1)" && echo -n "$(my_func_2)" && echo done
This newline is squashedThis newline is not squashed
done
$

Но покупатель будьте осторожны: как упоминалось в комментариях, это может хорошо работать для вывода, который просто идет на терминал, но если вы передаете это другому процессу, вы можете запутать его, поскольку он, вероятно, не будет ожидать странного завершения '\r'.

Я пытался обернуть голову вокруг этого, потому что я использовал bash для потоковой передачи в результате запуска интерпретатора на скрипте F#. После некоторых проб и ошибок это решило проблему:

$ cat fsi.ch
#!/bin/bash
echo "$(fsharpi --quiet --exec --nologo $1)"

$ fsi.ch messages.fsx
Welcome to my program. Choose from the menu:
new | show | remove

Предполагая, конечно, что вам нужно запустить терминальную программу. Надеюсь это поможет.

Другие вопросы по тегам