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


123

例如,给定:

USCAGoleta9311734.5021-120.1287855805

我只想提取:

US

6
感谢大家。我最终使用了“ cut -c1-2”,说实话,我什至不知道“ cut”在那儿。我想说我在命令行方面很有经验-但是显然我有很多东西要学习。
格雷格,

1
@Greg,请注意,cut是作为一个单独的过程运行的-它将比我在答案中发布的内部重击解决方案慢。除非您要处理大量数据集,否则这不会有任何区别,但您需要牢记这一点。
paxdiablo

编辑实际上,我认为每行报告此行代码可能会执行约50,000次。因此,我可能只使用内部Bash方法-如您所说,它将节省一些急需的资源。
格雷格,

Answers:


180

如果使用的是bashshell(根据您的评论似乎是这样),则最有效的方法可能是使用参数扩展的sub-string变体:

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

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

如果您要执行很多操作(如您提到的每个报告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-它们在输入中提供每行的前两个字符。

如果您确定您的输入是一行,则无所谓,它们都具有相同的效果。


我宁愿使用printf '%s'替代echo的情况下有字符串中的字符怪异:stackoverflow.com/a/40423558/895245对于POSIX痴迷:head -c是不是POSIX,cut -cawk substr是,sed \1不知道。
西罗Santilli郝海东冠状病六四事件法轮功

1
使用printf @CiroSantilli新疆改造中心996ICU六四事件,您甚至不需要其他程序。看我的回答
bschlueter

60

最简单的方法是

${string:position:length}

凡本提取$length将子$string$position

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


这是获取子字符串的最短,最甜蜜且最简单的方法。
ani627 '16

34

您已经获得了几个不错的答案,我将自己使用内置的Bash,但是由于您提出了问题sedawk并且(几乎)没有人提供基于它们的解决方案,因此我向您提供以下这些:

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

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

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

  • 替代“ s /”
  • 从任意字符“。”开始的“ ..”中的任意两个字符的“()”组,后跟任意字符“。” 重复零次或多次“ *”(需要使用反斜杠来转义某些特殊字符)
  • 用“ /”表示第一组(在这种情况下也是唯一)的内容(此处的反斜杠是特殊的转义符,表示匹配的子表达式)
  • 完成“ /”

1
在awk中,字符串从索引1开始,因此您应该使用substr($0,1,2)
艾萨克



6

您可以使用printf

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

5

科尔姆 —从文件中删除列

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

cat file | colrm 3

4

确实很晚,但是在这里

sed 's/.//3g'

要么

awk NF=1 FPAT=..

要么

perl -pe '$_=unpack a2'

2

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

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

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

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


最佳答案应该放在最前面。没有叉子,没有bashisms。即使在破折号这样的小贝壳上也可以使用。
7:58

1

如果您的系统使用的是其他外壳程序(不是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;"

这使用与主要答案相同的方法,仅bash在尚未使用时才调用。
palswim

不幸的是,这伴随着调用另一个过程的所有开销,但是有时开销与简单性和熟悉度无关。
palswim

1

只是为了好玩,我将添加一些内容,尽管它们过于复杂和无用,但并未提及它们:

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


0

如果mystring = USCAGoleta9311734.5021-120.1287855805

print substr(mystring,0,2)

会打印美国

其中0是开始位置,2是阅读方式


说...那不是GW-BASIC吗?哦,等等awk。抱歉,我一开始不能告诉。
暂停,直到另行通知。

0

这是你的追求吗?

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

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

参考:substr


1
因为他/她很可能从shell调用此,一种更好的方式是perl -e 'print substr $ARGV[0], 0, 2' 'USCAGoleta9311734.5021-120.1287855805'
查斯。欧文斯2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.