如何在shell脚本中提取字符串的前两个字符?

时间:2009-09-10 14:28:53

标签: bash shell grep sh gnu-coreutils

例如,给定:

USCAGoleta9311734.5021-120.1287855805

我想提取:

US

15 个答案:

答案 0 :(得分:150)

如果您使用bash shell(并且看起来是基于您的评论),可能最有效的方法是使用参数扩展的子字符串变体:

pax> long="USCAGol.blah.blah.blah"
pax> short="${long:0:2}" ; echo "${short}"
US

这会将short设置为long的前两个字符。如果long短于两个字符,short将与之相同。

这种shell内方法通常会更好,如果你要做很多事情(如你所提到的那样每报告50,000次),因为没有进程创建开销。所有使用外部程序的解决方案都会受到这种开销的影响。

如果您还想确保最小长度,您可以事先用以下方式填写:

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/')

对于单行字符串,前两个(cuthead)是相同的 - 它们基本上只返回前两个字符。它们的不同之处在于cut将为您提供每行的前两个字符,head将为您提供整个输入的前两个字符

第三个使用awk子字符串函数提取前两个字符,第四个使用sed捕获组(使用()\1)来捕获前两个字符并用它们替换整行。它们都与cut类似 - 它们在输入中传递每行的前两个字符。

如果你确定你的输入是单行,那么这些都没有关系,它们都具有相同的效果。

答案 1 :(得分:38)

最简单的方法是

${string:position:length}

这会从$length的{​​{1}}中提取$string子字符串。

这是一个内置的bash,因此不需要awk或sed。

答案 2 :(得分:31)

你已经得到了几个很好的答案,我自己也会使用Bash内置,但是因为你问过sedawk以及(几乎)没有人否则提供基于它们的解决方案,我为您提供这些:

echo "USCAGoleta9311734.5021-120.1287855805" | awk '{print substr($0,0,2)}'

echo "USCAGoleta9311734.5021-120.1287855805" | sed 's/\(^..\).*/\1/'

awk一个应该是相当明显的,但这里是对sed一个的解释:

  • 替换“s /”
  • 从“^”行开头的任何字符“..”中的两个字符串“()”,后跟任何字符“。”重复零次或多次“*”(需要使用反斜杠来逃避某些特殊字符)
  • by“/”第一个(也是唯一的,在这种情况下)组的内容(这里反斜杠是一个特殊的转义,指的是匹配的子表达式)
  • done“/”

答案 3 :(得分:7)

只是grep:

echo 'abcdef' | grep -Po "^.."        # ab

答案 4 :(得分:5)

如果您在bash,可以说:

bash-3.2$ var=abcd
bash-3.2$ echo ${var:0:2}
ab

这可能就是你所需要的......

答案 5 :(得分:4)

colrm - 从文件中删除列

要保留前两个字符,只需删除从3开始的列

cat file | colrm 3

答案 6 :(得分:4)

确实很晚但是在这里

sed 's/.//3g'

或者

awk NF=1 FPAT=..

或者

perl -pe '$_=unpack a2'

答案 7 :(得分:2)

只是出于娱乐目的,我会补充一点,尽管它们过于复杂和无用,但并未提及它们:

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]'

答案 8 :(得分:1)

如果您的系统使用的是其他shell(不是bash),但系统有bash,那么您仍然可以通过调用{{1}来使用bash的固有字符串操作变量:

bash

答案 9 :(得分:1)

如果您想使用shell脚本而不依赖于非posix扩展(例如所谓的bashisms),您可以使用不需要使用外部工具(如grep,sed,cut,awk等)的技术。 ,这会使你的脚本效率降低。在您的使用案例中,效率和posix可移植性可能并不重要。但是如果它(或者只是一个好习惯),您可以使用以下参数扩展选项方法来提取shell变量的前两个字符:

$ sh -c 'var=abcde; echo "${var%${var#??}}"'
ab

这使用“最小前缀”参数扩展删除前两个字符(这是${var#??}部分),然后“最小后缀”参数扩展${var%部分)从原始值中删除所有但前两个字符的字符串。

此方法先前在此answer中描述了“Shell =检查变量是否以#开头”的问题。该答案还描述了几个类似的参数扩展方法,这些方法可以在稍微不同的上下文中使用,这个上下文适用于原始问题。

答案 10 :(得分:1)

您可以使用printf

$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$orginal"
US

答案 11 :(得分:0)

这是你的事吗?

my $string = 'USCAGoleta9311734.5021-120.1287855805';

my $first_two_chars = substr $string, 0, 2;

参考:substr

答案 12 :(得分:0)

如果mystring = USCAGoleta9311734.5021-120.1287855805

print substr(mystring,0,2)

会打印美国

其中0表示起始位置,2表示如何使用meny chars进行阅读

答案 13 :(得分:0)

perl -ple 's/^(..).*/$1/'

答案 14 :(得分:0)

如何考虑 Unicode + UTF-8

让我们为那些对 Unicode 字符而不只是字节感兴趣的人做一个快速测试。 áéíóú (acute accented vowels) 的每个字符由 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

我们得到:

áéí
á
á
á

所以我们看到只有 awk + LC_CTYPE=en_US.UTF-8 考虑了 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

相关:https://unix.stackexchange.com/questions/3454/grabbing-the-first-x-characters-for-a-string-from-a-pipe

在 Ubuntu 21.04 上测试。