
Один из плагинов vim, который я использую, использует этот скрипт для передачи некоторых входных данных линтерам, которые не поддерживают чтение из stdin.
set -eu
# All of the following arguments are read as command to run.
file_extension="$1"
shift
temp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'ale_linter')
temp_file="$temp_dir/file$file_extension"
trap 'rm -r "$temp_dir"' EXIT
while read -r; do
echo "$REPLY" >> "$temp_file"
done
"$@" "$temp_file"
Сначала я был немного озадачен, почему они просто не использовали что-то подобное.
some input | some_program /dev/stdin
Но после того, как я попробовал ghc
его в качестве линтера, я обнаружил, что он жалуется на /dev/stdin, говоря что-то о том, что это не настоящий файл (что на самом деле не так)
Так что мне интересно, могу ли я использовать namedpipe вместо временного файла. Причина, по которой я не совсем удовлетворен записью во временные файлы, заключается в состоянии SSD, и если есть лучший способ сделать это, почему бы не сделать это, верно?
решение1
Нет, программы, которые отклоняют такие файлы, обычно отклоняют их на том основании, что файл не являетсяискомый(им нужно получить доступ к контенту с произвольными смещениями или несколько раз после перемотки и т. д.). Или они захотят открыть файл несколько раз. Они также могут захотеть перезаписать (часть) файла или обрезать его.
Безымянные pipe
(например, с |
и /dev/stdin
) или именованные не имеют значения ни в одном из этих случаев.
На самом деле, в Linux, /dev/stdin
когда stdin — это канал (с именем или без) и ведет себя точно так же, как именованный канал, программа не сможет отличить его /dev/stdin
от настоящего именованного канала.
В других системах это не совсем то же самое, но по сути открытие /dev/stdin
именованного канала даст вам файловый дескриптор канала, который невозможно найти ни тем, ни другим способом.
Итак, вам нужно создать временный файл. Обратите внимание, что некоторые оболочки делают это проще. С zsh
, это просто:
#! /bin/zsh -
"$@" =(cat)
В Linux и оболочках с использованием удаленных временных файлов для документов here (например bash
, zsh
и некоторых реализаций ksh
), вы можете сделать следующее:
#! /bin/bash -
"$@" /dev/fd/3 3<< EOF
$(cat)
EOF
Однако это может исказить содержимое файла, если он содержит символы NUL или заканчивается пустыми строками.
Обратите внимание, что начиная с версии 5, bash делает временный файл here doc доступным только для чтения, поэтому, если приложению необходимо внести изменения в этот файл, вам нужно будет восстановить права на запись с помощью:
#! /bin/bash -
{
chmod u+w /dev/fd/3 && # only needed in bash 5+
"$@" /dev/fd/3
} 3<< EOF
$(cat)
EOF
Заметка об этом while read
цикле, раз уж вы спросили.
First read -r
без имени переменной не является допустимым sh
синтаксисом. sh
Синтаксис указан POSIX (ISO 9945, также IEEE Std 1003.1), как и C
синтаксис указан ISO 9899.
Вэта спецификация, вы заметите, что read
требуется аргумент имени переменной. Поведение, когда вы его опускаете, следующее:неопределенныеи на практике различаются в зависимости от sh
реализации интерпретатора.
bash
является интерпретатором GNU sh
, как gcc
и компилятор GNU C. Оба bash
имеют gcc
расширения по сравнению с тем, что указано в этих стандартах.
В случае read
, bash
обрабатывается read -r
так, как если бы это было IFS= read -r REPLY
. В спецификации POSIX IFS= read -r REPLY
читает stdin до тех пор, пока не \n
будет достигнут символ или конец ввода, и сохраняет считанные символы в $REPLY
переменной и возвращается суспехстатус выхода, если был прочитан символ новой строки (полная строка) илиотказв противном случае (например, EOF перед символом новой строки) и оставляет поведение неопределенным, если считанные данные содержат символы NUL или последовательности байтов, которые не образуют допустимые символы.
В случае bash
он сохранит считанные байты, даже если они не образуют допустимые символы, и удалит символы NUL.
read -r
похож read -r REPLY
на ksh
или zsh
и сообщает об ошибке в POSIX-подобных оболочках на основе yash
или .ash
Поведение echo
не определено, если только его аргументы не содержат символы обратной косой черты и первый из них не является -n
.
Итак, подводя итог, если вы не знаете конкретную sh
реализацию (и версию), с которой имеете дело, вы не сможете сказать, что
while read -r; do
echo "$REPLY" >> "$temp_file"
done
подойдет. В bash
конкретном случае он сохранит stdin в temp_file только до тех пор, пока данные не содержат символы NUL, не заканчиваются символом новой строки и ни одна из строк не соответствует ^-[neE]+$
расширенному регулярному выражению (и/или в зависимости от среды или того, как bash
была скомпилирована, например, sh
OS/X, не содержит символы обратной косой черты).
Это такжеочень неэффективно и не так, как вы обрабатываете текст в оболочках.
Здесь вам нужно:
cat > "$temp_file"
cat
этостандартная команда, который при отсутствии аргументов просто выводит свой stdin на stdoutкак есть.