wva/sia/e1
我有、bct/e2
、形式的字符串sv/de/e11
。它总是 <Part1>/e<NUM>
或<Part1>/<Part2>/e<NUM>
。我想要通过保留各部分的首字母并去掉斜线和 e 来缩短字符串:
wva/sia/e1 > ws1
bct/e2 > b2
sv/de/e11 > sd11
我如何在 sh 脚本中做到这一点?
编辑:该字符串代表作业名称:
[...]
job_name=$1 # e.g. 'wva/sia/e1'
job_name=cut_name(job_name) # e.g. 'ws1'
[...]
答案1
形式为脚本正如你所要求的:
#!/usr/bin/env python3
import sys
# read the input, split by /
st = sys.argv[1].split("/")
# get the first char of all sections *but* the last one
# add the last *from* the first character
print("".join([s[0] for s in st][:-1])+st[-1][1:])
请注意,这适用于任何长度,例如:
wva/sia/bct/wva/sia/e1
会变成
wsbws1
只要最后一部分以/e<num>
使用
- 将脚本复制到一个空文件中,另存为
rearrange.py
使用字符串作为参数运行它,例如:
python3 /path/to/rearrange.py wva/sia/e1 > ws1
解释
脚本本身已经解释了一切,而且也进行了注释。
答案2
Bash 4.3 单行命令
假设我们不需要完整的脚本。Bash 具有足够的功能,允许我们使用一行代码。下面是其中的一行:
bash-4.3$ (read -r var ;IFS='/'; printf "%c" ${var%/*};echo ${var##*[^0-9]}) <<< "sv/de/e11"
sd11
怎么了 ?
- 一切都发生在子 shell 中,因此
( )
围绕整个命令 - 我们在这里使用字符串
<<<
发送输入,子shell命令通过它获取read -r var
并存储到var
变量中 - 我们设置
IFS='/'
子 shellvar
在分隔符处拆分成单独的项/
。这对于分词很重要。 - 接下来我们使用后缀删除
${var%/*}
来删除 之前的最后一部分/
。在上面的例子中,它将是e11
printf "%c"
${var%/*}
会看到由于上面提到的单词拆分和后缀删除而产生的结果sv de
(是的,很神奇)。由于printf
单词的格式,%c
将仅打印第一个字符,但它将对收到的每个命令行参数都这样做,因此sv de
它将输出s
和d
。打印时没有换行符,因此看起来好像字符是按顺序输入的echo ${var##*[^0-9]}
利用前缀删除来删除给定输入字符串中的所有非数字字符,从而仅获取最后一位数字
还有另一种单行方法,对于类似 C 语言的程序员来说,这种方法更加明确和自然。
bash-4.3$ (read -r inp;IFS='/';arr=( $inp ); for ((i=0;i<$(( ${#arr[@]} -1 ));i++));do printf "%s" ${arr[$i]:0:1};done;printf "%s\n" ${inp##*[^0-9]}) <<< "sv/de/e11"
sd11
这是什么魔法?解释如下:
- 所有事情都发生在子shell中,因此
()
围绕整个命令。 - 我们使用 here-string
<<<
将我们想要的项目发送到命令的标准输入流中,命令通过read -r inp
命令获取它并将其存储到inp
变量中 - 接下来我们改变
IFS
变量,以便我们可以将所有内容分解为一个数组。 - 我们使用 C 风格的 for 循环遍历所有项,直到倒数第二项
for ((initial condition; test condition; post condition)) ; do ... done
- 这
$(( ${#arr[@]} - 1 ))
是算术扩展,我们从数组的长度中减去 1${#arr[@]}
- 这
printf "%s" ${arr[$i]:0:1}
允许我们使用参数扩展,其中我们只打印每个项目的第一个字符,并且printf "%s"
不带换行符打印,因此看起来我们在同一行上打印每个字母。 - 最后,循环结束后,我们获取原始输入文本,并使用前缀删除功能删除所有非数字内容
${#*[^0-9]}
脚本方法
由于问题要求使用 shell 脚本,这里是bash
4.3 中的一个脚本,它与上面的方法几乎相同,但更明确:
#!/bin/bash
IFS='/'
items=( $1 )
counter=1
for i in ${items[@]}
do
if [ $counter -eq ${#items[@]} ];
then
# note the space before -1
printf "%s\n" "${i##*[^0-9]}"
else
printf "%s" "${i:0:1}"
fi
counter=$(($counter + 1))
done
其工作方式如下:
- 在命令行上给定一个字符串作为参数,我们将内部字段分隔符设置为
/
,并允许 bash 执行单词拆分以将字符串分解为名为的数组items
- 我们迭代数组中的所有项目
${items[@]}
,同时使用计数器变量跟踪我们所处的项目,并了解数组中的项目数量(部分${#items[@]}
)。 - 这
if-statement
使得我们可以从每个项目中挑选出特定的字符。使用参数扩展,第一个字符通过${i:0:1}
。使用最长前缀删除${variable##prefix}
,我们从中的最后一个字符串中删除所有非数字字符printf "%s\n" "${i##*[^0-9]}"
。
实际操作如下:
$ ./shorten_string.sh "wva/sia/e1"
ws1
$ ./shorten_string.sh "bct/e2"
b2
$ ./shorten_string.sh "sv/de/e11"
sd11
答案3
好的,这不是脚本,但你可以把它放在脚本中(这也是非常不优雅的,因为我无法在一个命令中处理这两种形式)
$ sed -r 's:(.).*/(.).*/e([0-9]+):\1\2\3:;s:(.).*/e([0-9]+):\1\2:' file
ws1
b2
sd11
笔记
-r
使用 EREs:old:new:
old
用。。。来代替new
.*
任意数量的任意字符(.)
在此位置保存一个字符([0-9]+)
请在此处保存至少一个数字;
分隔命令,就像在 shell 中一样\1
反向引用使用以下方式保存的字符()