Как извлечь первые два символа строки в сценарии оболочки?
Например, учитывая:
USCAGoleta9311734.5021-120.1287855805
Я хочу извлечь только:
US
15 ответов
Вероятно, самый эффективный метод, если вы используете bash
Оболочка (и вы, по-видимому, основываясь на ваших комментариях) должна использовать вариант расширения параметров в виде строки:
pax> long="USCAGol.blah.blah.blah"
pax> short="${long:0:2}" ; echo "${short}"
US
Это установит short
быть первыми двумя персонажами long
, Если long
короче двух символов, short
будет идентичным этому.
Этот метод в оболочке обычно лучше, если вы собираетесь делать это много (например, 50000 раз на отчет, как вы упомянули), поскольку нет затрат на создание процесса. Все решения, которые используют внешние программы, пострадают от этих накладных расходов.
Если вы также хотите обеспечить минимальную длину, вы можете добавить ее заранее:
pax> long="A"
pax> tmpstr="${long}.."
pax> short="${tmpstr:0:2}" ; echo "${short}"
A.
Это гарантирует, что все, что меньше двух символов в длину, будет дополнено справа точками (или что-то еще, просто изменив символ, используемый при создании tmpstr
). Непонятно, что вам это нужно, но я подумал, что я добавлю это для полноты.
Сказав, что есть несколько способов сделать это с внешними программами (например, если у вас нет bash
доступны для вас), некоторые из которых являются:
short=$(echo "${long}" | cut -c1-2)
short=$(echo "${long}" | head -c2)
short=$(echo "${long}" | awk '{print substr ($0, 0, 2)}'
short=$(echo "${long}" | sed 's/^\(..\).*/\1/')
Первые два (cut
а также head
) одинаковы для однострочной строки - в основном оба возвращают первые два символа. Они отличаются тем, что cut
даст вам первые два символа каждой строки и head
даст вам первые два символа всего ввода
Третий использует awk
функция подстроки, чтобы извлечь первые два символа и четвертый использует sed
захватывать группы (используя ()
а также \1
) захватить первые два символа и заменить ими всю строку. Они оба похожи на cut
- они доставляют первые два символа каждой строки на входе.
Ничто из этого не имеет значения, если вы уверены, что ваш ввод состоит из одной строки, все они имеют одинаковый эффект.
Самый простой способ
${string:position:length}
Где это выдержки $length
подстрока из $string
в $position
,
Это встроенный bash, поэтому awk или sed не требуются.
Вы получили несколько хороших ответов, и я бы сам поработал над встроенным Bash, но так как вы спросили о sed
а также awk
и (почти) никто не предлагал решения на их основе, я предлагаю вам следующее:
echo "USCAGoleta9311734.5021-120.1287855805" | awk '{print substr($0,0,2)}'
а также
echo "USCAGoleta9311734.5021-120.1287855805" | sed 's/\(^..\).*/\1/'
awk
один должен быть довольно очевидным, но вот объяснение sed
один:
- заменить "S /"
- группа "()" из двух любых символов "..", начинающаяся в начале строки "^", за которой следует любой символ "." повторяется ноль или более раз "*" (обратная косая черта необходима для экранирования некоторых специальных символов)
- "/" содержимое первой (и единственной, в данном случае) группы (здесь обратный слеш - это специальный escape, ссылающийся на соответствующее подвыражение)
- сделанный "/"
Если вы хотите использовать сценарии оболочки и не полагаться на не-posix-расширения (такие как так называемые bashisms), вы можете использовать методы, которые не требуют разветвления внешних инструментов, таких как grep, sed, cut, awk и т. Д., Которые затем сделать ваш сценарий менее эффективным. Может быть, в вашем случае эффективность и переносимость posix не важны. Но в случае, если это так (или просто как хорошая привычка), вы можете использовать следующий метод расширения параметра для извлечения первых двух символов переменной оболочки:
$ sh -c 'var=abcde; echo "${var%${var#??}}"'
ab
При этом используется расширение параметра "наименьший префикс" для удаления первых двух символов (это ${var#??}
часть), затем расширение параметра "наименьший суффикс" (${var%
часть), чтобы удалить эту строку "все, кроме первых двух символов" из исходного значения.
Этот метод был ранее описан в этом ответе на вопрос "Shell = Проверить, если переменная начинается с #". В этом ответе также описывается пара похожих методов расширения параметров, которые можно использовать в несколько ином контексте, чем тот, который применяется к исходному вопросу здесь.
Если вы в bash
, ты можешь сказать:
bash-3.2$ var=abcd
bash-3.2$ echo ${var:0:2}
ab
Это может быть именно то, что вам нужно...
Вы можете использовать printf
:
$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$original"
US
colrm - удалить столбцы из файла
Чтобы оставить первые два символа, просто удалите столбцы, начиная с 3
cat file | colrm 3
Довольно поздно, но вот оно
sed 's/.//3g'
Или же
awk NF=1 FPAT=..
Или же
perl -pe '$_=unpack a2'
Ради интереса я добавлю несколько, которые, хотя они слишком сложные и бесполезные, не упоминались:
head -c 2 <( echo 'USCAGoleta9311734.5021-120.1287855805')
echo 'USCAGoleta9311734.5021-120.1287855805' | dd bs=2 count=1 status=none
sed -e 's/^\(.\{2\}\).*/\1/;' <( echo 'USCAGoleta9311734.5021-120.1287855805')
cut -c 1-2 <( echo 'USCAGoleta9311734.5021-120.1287855805')
python -c "print(r'USCAGoleta9311734.5021-120.1287855805'[0:2])"
ruby -e 'puts "USCAGoleta9311734.5021-120.1287855805"[0..1]'
Как учитывать Unicode + UTF-8
Давайте проведем быстрый тест для тех, кто интересуется символами Unicode, а не просто байтами. Каждый персонаж
áéíóú
(гласные с острым ударением ) состоит из двух байтов в кодировке UTF-8. С:
printf 'áéíóú' | LC_CTYPE=en_US.UTF-8 awk '{print substr($0,1,3);exit}'
printf 'áéíóú' | LC_CTYPE=C awk '{print substr($0,1,3);exit}'
printf 'áéíóú' | LC_CTYPE=en_US.UTF-8 head -c3
echo
printf 'áéíóú' | LC_CTYPE=C head -c3
мы получили:
áéí
á
á
á
Итак, мы видим, что только + считал символы UTF-8. Другие подходы занимали всего три байта. Мы можем подтвердить это с помощью:
printf 'áéíóú' | LC_CTYPE=C head -c3 | hd
который дает:
00000000 c3 a1 c3 |...|
00000003
и
c3
сам по себе является мусором и не отображается на терминале, поэтому мы видели только
á
.
awk
+
LC_CTYPE=en_US.UTF-8
однако фактически возвращает 6 байтов.
Мы также могли бы провести эквивалентное тестирование с помощью:
printf '\xc3\xa1\xc3\xa9\xc3\xad\xc3\xb3\xc3\xba' | LC_CTYPE=en_US.UTF-8 awk '{print substr($0,1,3);exit}'
и если вам нужен общий параметр:
n=3
printf 'áéíóú' | LC_CTYPE=en_US.UTF-8 awk "{print substr(\$0,1,$n);exit}"
Более конкретный вопрос о Unicode + UTF-8: https://superuser.com/questions/450303/unix-tool-to-output-first-n-characters-in-an-utf-8-encoded-file
Проверено на Ubuntu 21.04.
Если ваша система использует другую оболочку (не bash
), но ваша система имеет bash
, тогда вы все еще можете использовать присущие строки манипуляции bash
вызывая bash
с переменной:
strEcho='echo ${str:0:2}' # '${str:2}' if you want to skip the first two characters and keep the rest
bash -c "str=\"$strFull\";$strEcho;"
Это то, что вы после?
my $string = 'USCAGoleta9311734.5021-120.1287855805';
my $first_two_chars = substr $string, 0, 2;
ref: substr
if mystring = USCAGoleta9311734.5021-120.1287855805
print substr(mystring,0,2)
будет печатать США
где 0 - начальная позиция, а 2 - как читать символы