例如,给定:
USCAGoleta9311734.5021-120.1287855805
我想提取:
US
答案 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/')
对于单行字符串,前两个(cut
和head
)是相同的 - 它们基本上只返回前两个字符。它们的不同之处在于cut
将为您提供每行的前两个字符,head
将为您提供整个输入的前两个字符
第三个使用awk
子字符串函数提取前两个字符,第四个使用sed
捕获组(使用()
和\1
)来捕获前两个字符并用它们替换整行。它们都与cut
类似 - 它们在输入中传递每行的前两个字符。
如果你确定你的输入是单行,那么这些都没有关系,它们都具有相同的效果。
答案 1 :(得分:38)
最简单的方法是
${string:position:length}
这会从$length
的{{1}}中提取$string
子字符串。
这是一个内置的bash,因此不需要awk或sed。
答案 2 :(得分:31)
你已经得到了几个很好的答案,我自己也会使用Bash内置,但是因为你问过sed
和awk
以及(几乎)没有人否则提供基于它们的解决方案,我为您提供这些:
echo "USCAGoleta9311734.5021-120.1287855805" | awk '{print substr($0,0,2)}'
和
echo "USCAGoleta9311734.5021-120.1287855805" | sed 's/\(^..\).*/\1/'
awk
一个应该是相当明显的,但这里是对sed
一个的解释:
答案 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
在 Ubuntu 21.04 上测试。