Получение заголовка для отображения всех, кроме последней строки файла: замена команд и стандартное перенаправление ввода-вывода
Я пытаюсь заставить утилиту head
отображать все, кроме последней строки стандартного ввода. Фактический код, который мне нужен, - это что-то вроде строки cat myfile.txt | head -n $(($(wc -l)-1))
. Но это не сработало. Я делаю это на Darwin/OS X, у которого нет хорошей семантики head -n -1
, которая бы получила мне подобный вывод.
Ни один из этих вариантов не работает.
cat myfile.txt | head -n $(wc -l | sed -E -e 's/\s//g')
echo "hello" | head -n $(wc -l | sed -E -e 's/\s//g')
Я тестировал больше вариантов и, в частности, нашел, что это работает:
cat <<EOF | echo $(($(wc -l)-1))
>Hola
>Raul
>Como Esta
>Bueno?
>EOF
3
Здесь работает нечто более простое.
echo "hello world" | echo $(($(wc -w)+10))
Это понятно, что я ошибочно ошибаюсь. Но это, по крайней мере, говорит мне, что программа head
не потребляет стандартный ввод, прежде чем передавать материал на подзаголовок/подменю команды, удаленная возможность, но та, которую я хотел все равно исключить.
echo "hello" | head -n $(cat && echo 1)
Что объясняет поведение head
и wc
и их взаимодействие через подоболочки здесь? Благодарим за помощь.
Ответы
Ответ 1
head
- неправильный инструмент. Если вы хотите увидеть все, кроме последней строки, используйте:
sed \$d
Причина, по которой
# Sample of incorrect code:
echo "hello" | head -n $(wc -l | sed -E -e 's/\s//g')
сбой заключается в том, что wc
потребляет весь вход, и для head
ничего не осталось. wc
наследует свой stdin из подоболочки, в которой он запущен, который считывается с вывода echo
. Когда он потребляет вход, он возвращается, а затем head
пытается прочитать данные... но все это исчезло. Если вы хотите прочитать ввод дважды, данные должны быть где-то сохранены.
Ответ 2
head -n -1
предоставит вам все, кроме последней строки его ввода.
Ответ 3
Использование sed:
sed '$d' filename
удалит последнюю строку файла.
$ seq 1 10 | sed '$d'
1
2
3
4
5
6
7
8
9
Ответ 4
cat myfile.txt | echo $(($(wc -l)-1))
Это работает. Это слишком сложно: вы можете просто написать echo $(($(wc -l)-1)) <myfile.txt
или echo $(($(wc -l <myfile.txt)-1))
. Проблема заключается в том, как вы ее используете.
cat myfile.txt | head -n $(wc -l | sed -E -e 's/\s//g')
wc
потребляет все входные данные при подсчете строк. Таким образом, к моменту запуска head
нет данных, которые нужно прочитать в трубе.
Если ваш вход поступает из файла, вы можете перенаправить оба wc
и head
из этого файла.
head -n $(($(wc -l <myfile.txt) - 1)) <myfile.txt
Если ваши данные могут поступать из трубы, вам необходимо ее дублировать. Обычный инструмент для дублирования потока - tee
, но этого здесь недостаточно, потому что два выхода из tee
создаются с одинаковой скоростью, тогда как здесь wc
нужно полностью потреблять свой выход до head
может начаться. Поэтому вместо этого вам нужно будет использовать один инструмент, который может обнаружить последнюю строку, что является более эффективным подходом.
Удобно, sed предлагает способ сопоставления последней строки. Любая печать всех строк, кроме последней, или подавление последней выходной строки, будет работать:
sed -n '$! p'
sed '$ d'
Ответ 5
В Mac OS X я нашел ответ от комментария к этому Q & A.
Предполагая, что вы используете Homebrew, запустите brew install coreutils
, затем используйте команду ghead
:
cat myfile.txt | ghead -n -1
Или, что эквивалентно:
ghead -n -1 myfile.txt
Наконец, см. brew info coreutils
, если вы хотите использовать команды без префикса g
(например, head
вместо ghead
).
Ответ 6
awk 'NR>1{print p}{p=$0}'
Для этого задания awk one-liner немного длиннее, чем sed.