получить первые X символов из команды cat?


42

У меня есть текстовый файл, который я выводил в переменную в моем сценарии оболочки. Однако мне нужны только первые 50 символов.

Я пытался использовать, cat ${filename} cut -c1-50но я получаю гораздо больше, чем первые 50 символов? Это может быть связано с cutпоиском строк (не уверен на 100%), хотя этот текстовый файл может быть одной длинной строкой - это действительно зависит.

Есть ли какая-нибудь утилита, в которую я могу обратиться, чтобы получить первые X символов из catкоманды?


10
Вы забыли |? cat ${filename} | cut -c1-50
DisplayName

@DisplayName исправлено, спасибо, что поймал мою ошибку при перепечатывании.
jkj2000

1
@ jkj2000, я вернулся к старой версии, так как это был оригинальный вопрос.
Рамеш

Ответы:


61
head -c 50 file

Это возвращает первые 50 байтов.

Имейте в виду, что команда не всегда выполняется одинаково на всех ОС. В Linux и macOS это ведет себя так. В Solaris (11) вам нужно использовать версию gnu в / usr / gnu / bin /


голова не имеет -cвыбора. Я бы пошел на дд (1) вместо этого.
Мирабилось

7
Обратите внимание, что в этом ответе предполагается, что файл содержит только символы ASCII, поскольку OP запрашивает первые X символов, а не байты.
Calimo

2
@mirabilos Это может быть не переносимо, но моя версия ( GNU coreutils 5.97) делает.
Йоссариан

1
Однако POSIX не определяет -cдопустимую опцию, поэтому он определенно зависит от вашей локальной среды. unix.com/man-page/posix/1/head
Жюль

1
@ Calimo Да, я знаю, но я попытался создать текстовый файл из 100 символов, затем выполнил мою команду, и она напечатала 50 символов. Но вы правы насчет ASCII, но так как OP пометил это как ответ, в его случае ничего не было.
DisplayName

27

Ваша cutкоманда работает, если вы используете канал для передачи данных:

cat ${file} | cut -c1-50 

Или, избегая бесполезного использования кошки и делая его немного более безопасным:

cut -c1-50 < "$file"

Обратите внимание, что команды выше будут печатать первые 50 символов (или байтов, в зависимости от вашей cutреализации) каждой строки ввода . Он должен делать то, что вы ожидаете, если, как вы говорите, ваш файл представляет собой одну огромную строку.


8
dd status=none bs=1 count=50 if=${filename}

Это возвращает первые 50 байтов.


У дд нет status=noneфлага. 2>/dev/nullВместо этого используйте (и правильно цитируйте): dd if="$filename" bs=1 count=50 2>/dev/null(несмотря на это, рассмотрите возможность использования bs=50 count=1для уменьшения количества задействованных системных вызовов).
Мирабилось

1
@mirabilos dd действительно имеет status=noneпри использовании Ubuntu 14.04, coreutils 8.21, но вы можете использовать его, 2>/dev/nullесли используете более раннюю версию.
doneal24

1
@mirabilos Большинство дистрибутивов Linux используют GNU coreutils, как и FreeBSD и другие BSD. Он доступен на Solaris в виде пакета gnu-coreutils. Да, это "Unix & Linux", и системы Unix и Linux используют GNU coreutils.
doneal24

2
Нет, системы Unix обычно не используют утилиты GNU. GNU является аббревиатурой от «GNU не Unix», даже. Пожалуйста, придерживайтесь переносимых решений или, если вам необходимо предоставить решения только для GNU, укажите это и, если возможно, покажите эквивалентное переносимое решение.
Мирабилось

1
Строго говоря, это делает один read()из 50 байтов. Если, fileнапример, это канал, и в то время доступно меньше символов, будет возвращено меньше байтов. Чтобы иметь эквивалент head -c50, вам нужно использовать специфический для GNU iflag=fullblock.
Стефан Шазелас

4

Большинство ответов до сих пор предполагают, что 1 байт = 1 символ, что может быть не так, если вы используете не ASCII-локаль.

Немного более надежный способ сделать это:

testString=$(head -c 200 < "${filename}") &&
  printf '%s\n' "${testString:0:50}"

Обратите внимание, что это предполагает:

  1. Вы используете ksh93, bash(или в последнее время zshили mksh(хотя только многобайтовой кодировка поддерживается mkshв UTF-8 и только после того, как set -o utf8-mode)) и версия , headкоторая поддерживает -c(большинство из них в настоящее время, но не строго стандарт).
  2. Для текущей локали задана та же кодировка, что и для файла (введите locale charmapи file -- "$filename"проверьте это); если нет, установите его с помощью ie. LC_ALL=en_US.UTF-8)
  3. Я взял первые 200 байтов файла head, предполагая наихудший вариант UTF-8, где все символы закодированы не более чем в 4 байта. Это должно охватывать большинство случаев, о которых я могу думать.

Конечно, это также предполагает использование GNU headили другой его реализации, в которой добавлена ​​опция nōn-standard -c. Но вам уже нужна GNU bash. (Примечание: mkshрежим UTF-8 мог бы сделать это для файлов в кодировке UTF-8.) Я бы спросил у OP, требуют ли они октеты или многобайтовые символы, просто «символы» - это неопределенный термин.
Мирабилось

Это также предполагает $filenameили $testStringне содержит пустых символов новой строки, подстановочных знаков или начинается с -.
Стефан Шазелас

${var:offset:length}Конструкция вы используете здесь на самом деле происходит от ksh93и поддерживается последними версиями zsh( zshимеет свой собственный $testString[1,50]). Вы нуждаетесь ${testString:0:50} в ksh93и zshоднако.
Стефан Шазелас

Только отредактировал мой ответ, чтобы ответить на комментарии выше
Calimo

2
grep -om1 "^.\{50\}" ${filename}

Другой вариант (для первой строки в файле)

(IFS= read -r line <${filename}; echo ${line:0:50})

Это злоупотребление высокоуровневыми инструментами, и они склонны делать не то, что вам нужно, например, если они осведомлены о локали.
Мирабилось

@mirabilos Что вы имеете в виду под инструментами высокого уровня : readа echo? Или bash expansion?
Костас

grep(regexp), и да, использование здесь оболочки (подсказка: первая строка может быть большой). (Это, как говорится, bashism также не в POSIX, но большинство оболочек реализуют это.)
mirabilos

0

1. Для файлов ASCII, сделайте, как @DisplayName говорит:

head -c 50 file.txt

например, распечатает первые 50 символов файла file.txt.

2. Для двоичных данных hexdumpраспечатайте их в виде шестнадцатеричных символов:

hexdump -n 50 -v file.bin

например, распечатает первые 50 байтов файла file.bin.

Обратите внимание, что без параметра -vverbose hexdumpвместо повторяющихся строк будет использоваться звездочка ( *). Смотрите здесь: https://superuser.com/questions/494245/what-does-an-asterisk-mean-in-hexdump-output/494613#494613 .


-2

Вы можете использовать sed для этого, что довольно легко решит проблему

sed -e 's/^\(.\{50\}\).*/\1/' yourfile

Любопытно узнать , как это получили downvoted , если он решает вопрос в OP: «Я нужен только первые 50 символов» This выполняет то , что было запрошенным без UUOC (Бесполезное Использование Cat)
munkeyoto

1
Этот ответ дает первые пятьдесят символов каждой строки в файле, а не только первые 50 символов файла. Также не печатает вообще ничего, если все строки имеют длину менее 50 символов. Ваше решение будет работать лучше сsed -n -e '1s/^\(.\{50\}\).*/\1/p' ${filename}
doneal24

Понять можно было просто: head -n 1 | sed -e 's / ^ (. \ {50 \}). * / \ 1 /' ... И это решило бы проблему. ОП констатировал: «нужны только первые 50 символов»
munkeyoto

1
Нет. Если первая строка длиной всего 49 символов, она ничего не выдаст.
doneal24

Даг, я понял это в первый раз, но ОП ничего не сказал о печати, если строка содержала менее 50 символов, поэтому я до сих пор не вижу твоей точки зрения и смысла опровергать это мнение, так как снова попал в то, что сработало бы с head: head -n 1 $ {имя файла} | sed -n -e '1s / ^ (. \ {50 \}). * / \ 1 / p'
munkeyoto
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.