用bash索引字符串


15

如何在sh / bash中按索引引用字符串?也就是说,基本上将其拆分。

我正在尝试删除文件名的5个字符。所有名称都具有以下结构:name_nr_code。我正在尝试删除5个字母数字代码位。name_nr_始终为10个字符。

有没有类似的事情?

for i in * ; do mv "$i" "$i"[:10] ; done


5
bash如果您要寻求sh解决方案,为什么要加上标签?
斯特凡Chazelas

Answers:


15

就这么简单。

(重击)

for i in * ; do mv -- "$i" "${i:0:5}" ; done

以及《高级Bash脚本指南》第10章,操作变量中的解释(带有额外的NOTEs内联以突出显示该手册中的错误):

子串提取

${string:position}

从提取子串$string$position

如果$string参数是“ *”或“ @”,则将从处提取位置参数$position

${string:position:length}

$length$stringat 提取子字符串的字符$position

NOTE参数扩展周围缺少引号! echo不应用于任意数据。

stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}                       # abcABC123ABCabc
echo ${stringZ:1}                       # bcABC123ABCabc
echo ${stringZ:7}                       # 23ABCabc 

echo ${stringZ:7:3}                     # 23A
                                        # Three characters of substring.


# Is it possible to index from the right end of the string?

echo ${stringZ:-4}                      # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . . 

echo ${stringZ:(-4)}                    # Cabc
echo ${stringZ: -4}                     # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.

位置长度的参数可以被“参数化”,即,表示为一个变量,而不是作为一个数值常数。


如果$string参数为“ *”或“ @”,则将$length从处提取最大的位置参数$position

echo ${*:2}          # Echoes second and following positional parameters.
echo ${@:2}          # Same as above.

echo ${*:2:3}        # Echoes three positional parameters, starting at second.

NOTEexpr substr是GNU扩展。

expr substr $string $position $length

$length$string开始于提取字符$position

stringZ=abcABC123ABCabc
#       123456789......
#       1-based indexing.

echo `expr substr $stringZ 1 2`           # ab
echo `expr substr $stringZ 4 3`           # ABC

NOTE:这echo是多余的,因此可靠性甚至更低。使用expr substr + "$string1" 1 2

NOTEexpr如果输出为0(或-0,00 ...),则返回非零退出状态。


顺便说一句。这本书在官方Ubuntu资料库中以形式出现abs-guide


说“位置”有点误导,因为它实际上是一个偏移量,这意味着它${var:1}不会var从“第一位置” 返回值,而是从第二位置返回。
库沙兰丹

没错,但只要您不同意,就可以排名第零。对我来说很好。

9

在POSIX中sh

  • "${var%?????}"$var剥离的最后5个尾部字符的(或$var如果$var包含少于5个字符)

  • "${var%"${var#??????????}"}"是的前10个字符$var

  • "${var%_*}"$var剥离匹配的最短串的_*在端部$varfoo_bar_baz- > foo_bar)。
  • "${var%%_*}":相同但最长的匹配,而不是最短的匹配(foo_bar_baz-> foo)。
  • 如果你想获得foo_bar_"${var%"${var##*_}"}"${var##pattern}是一样的${var%%pattern},但找之初的模式$var,而不是末端)。

zsh

  • $var[1,-6] 第一个字符到末尾的第6个字符(因此,最后5个字符除外)。
  • $var[1,10] 前10个字符。

随着kshbashzsh

  • "${var:0:10}":的前10个字符 $var

bashzsh

  • "${var:0:-5}":除最后5个字符外的所有字符(如果$var设置了字符但包含少于5个字符(如果$var未使用设置的话zsh),也会出错并退出脚本)。

如果您需要Bourne sh兼容性,则很难可靠地进行。如果可以保证结果不会以换行符结尾,则可以执行以下操作:

first_10=`expr " $var" : ' \(.{1,10\}\)'` # beware the exit status
                                          # may be non-zero if the
                                          # result is 0 or 0000000000

all_but_last_5=`expr " $var" : ' \(.*\).\{5\}'`

您还将限制$var(在系统之间变化)的长度。

在所有这些解决方案中,如果$var包含的字节不能构成有效字符的一部分,则为YMMV。


我的,他们的确在括号内提出了一些丑陋的语法。

2

sh没有提供从字符串中获取子字符串的内置方法(据我所知),但是bash您可以这样做

${i:0:10}

这将为您提供变量值的前十个字符i

一般格式为${variable:offset:length}


2

大多数外壳程序都支持某种可以帮助您的参数扩展。在bash中,您可以使用

substr=${string:4:5} # start at position 4, length 5.

在中dash,不支持偏移量,但是您可以使用前导和尾随模式:

remove_first3=${string#???}
remove_last2=${string%??}

0

首先,不要for对文件名使用循环。

然后,这样的事情应该会有所帮助。

find ./ -type f | while read filename ;do
  newfilename=$(echo ${filename}|cut -c 1-10)
  mv ${filename} ${newfilename}
done

3
为什么对for文件名使用不好?
choroba

引用您的变量并使用printf起来更安全。...和read -r
库萨兰达

3
OP的for循环很好,但可能缺少它--。在您的4行代码中,我至少可以看到10个错误!其中许多众所周知的坏习惯像假设文件名都是单行线,利用回声,缺少报价
斯特凡Chazelas
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.