例如,给定:
USCAGoleta9311734.5021-120.1287855805
我只想提取:
US
如果您使用的是bash
shell(根据您的评论,您似乎是),可能最有效的方法是使用参数扩展的子字符串变体:
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/')
前两个 (cut
和head
) 对于单行字符串是相同的 - 它们基本上都只是返回前两个字符。它们的不同之处在于cut
将为您提供每行的前两个字符,并head
为您提供整个输入的前两个字符
第三个使用awk
sub-string 函数提取前两个字符,第四个使用sed
捕获组(使用()
and \1
)捕获前两个字符并用它们替换整行。它们都类似于cut
- 它们提供输入中每行的前两个字符。
如果您确定输入是单行,那么这些都不重要,它们都具有相同的效果。
最简单的方法是:
${string:position:length}
$length
这从$string
at 中提取子字符串$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
:
只需 grep:
echo 'abcdef' | grep -Po "^.." # ab
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>
您可以使用printf
:
$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$original"
US
如果你想使用 shell 脚本而不依赖于非 posix 扩展(例如所谓的 bashisms),你可以使用不需要分叉外部工具的技术,例如 grep、sed、cut、awk 等,然后使您的脚本效率降低。也许效率和 posix 可移植性在您的用例中并不重要。但如果是这样(或者只是作为一个好习惯),您可以使用以下参数扩展选项方法来提取 shell 变量的前两个字符:
$ sh -c 'var=abcde; echo "${var%${var#??}}"'
ab
这使用“最小前缀”参数扩展来删除前两个字符(这是${var#??}
部分),然后使用“最小后缀”参数扩展(${var%
部分)从原始字符串中删除除前两个字符之外的所有字符串价值。
此方法之前已在“Shell = 检查变量是否以 # 开头”问题的答案中进行了描述。该答案还描述了一些类似的参数扩展方法,可以在与此处适用于原始问题的上下文略有不同的上下文中使用。
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]'
如果您的系统使用的是不同的 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;"
这可能是你所追求的:
my $string = 'USCAGoleta9311734.5021-120.1287855805';
my $first_two_chars = substr $string, 0, 2;
参考:substr
如何考虑 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
在 Ubuntu 21.04 上测试。
编码
if mystring = USCAGoleta9311734.5021-120.1287855805
print substr(mystring,0,2)
将打印美国。
其中 0 是起始位置,2 是要读取的字符数。
perl -ple 's/^(..).*/$1/'