144

例如,给定:

USCAGoleta9311734.5021-120.1287855805

我只想提取:

US
4

15 回答 15

211

如果您使用的是bashshell(根据您的评论,您似乎是),可能最有效的方法是使用参数扩展的子字符串变体:

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为您提供整个输入的前两个字符

第三个使用awksub-string 函数提取前两个字符,第四个使用sed捕获组(使用()and \1)捕获前两个字符并用它们替换整行。它们都类似于cut- 它们提供输入中每行的前两个字符。

如果您确定输入是单行,那么这些都不重要,它们都具有相同的效果。

于 2009-09-10T14:32:36.890 回答
71

最简单的方法是:

${string:position:length}

$length这从$stringat 中提取子字符串$position

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

于 2009-09-10T14:31:13.570 回答
36

你已经得到了几个很好的答案,我会自己使用内置的 Bash,但是由于你询问sedawk几乎)没有其他人提供基于它们的解决方案,我为你提供这些:

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

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

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

  • 替换“s/”
  • 由两个任意字符“..”组成的组“()”,从行首“^”开始,后跟任意字符“.” 重复零次或多次“*”(需要反斜杠来转义某些特殊字符)
  • 通过“/”表示第一个(在这种情况下也是唯一的)组的内容(这里的反斜杠是指匹配子表达式的特殊转义)
  • 完毕 ”/”
于 2009-09-10T15:40:31.377 回答
10

只需 grep:

echo 'abcdef' | grep -Po "^.."        # ab
于 2017-01-02T18:33:50.700 回答
9

If you're in bash, you can say:

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

This may be just what you need…</p>

于 2009-09-10T16:35:06.170 回答
7

您可以使用printf

$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$original"
US
于 2019-06-13T17:37:35.363 回答
7

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

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

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

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

于 2018-03-25T22:42:56.780 回答
5

colrm — 从文件中删除列

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

cat file | colrm 3
于 2009-09-10T15:44:59.073 回答
4

利用:

sed 's/.//3g'

或者

awk NF=1 FPAT=..

或者

perl -pe '$_=unpack a2'
于 2013-04-19T01:27:33.573 回答
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]'
于 2020-05-16T01:23:02.600 回答
1

如果您的系统使用的是不同的 shell(不是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;"
于 2017-01-23T20:43:02.093 回答
0

这可能是你所追求的:

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

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

参考:substr

于 2009-09-10T14:32:55.640 回答
0

如何考虑 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

我们得到:

áéí
á
á
á

所以我们看到只有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 上测试。

于 2021-07-11T10:50:43.017 回答
-1

编码

if mystring = USCAGoleta9311734.5021-120.1287855805

    print substr(mystring,0,2)

将打印美国。

其中 0 是起始位置,2 是要读取的字符数。

于 2009-09-10T14:33:14.347 回答
-1
perl -ple 's/^(..).*/$1/'
于 2009-09-10T14:44:53.530 回答