我正在尝试提取文件名的一部分 - 日期和后缀之前的所有内容。我不确定在bashscript中执行此操作的最佳方法。正则表达式?
名称是文件名的一部分。我试图将它存储在一个shellscript变量中。前缀不包含奇怪的字符。后缀将是相同的。文件存储在一个目录中 - 我将使用循环来提取每个文件的文件名部分。
预期的输入文件:
EXAMPLE_FILE_2017-09-12.out
EXAMPLE_FILE_2_2017-10-12.out
预期摘录:
EXAMPLE_FILE
EXAMPLE_FILE_2
尝试:
filename=$(basename "$file")
folder=sed '^s/_[^_]*$//)' $filename
echo 'Filename:' $filename
echo 'Foldername:' $folder
答案 0 :(得分:1)
$ cat file.txt
EXAMPLE_FILE_2017-09-12.out
EXAMPLE_FILE_2_2017-10-12.out
$
$ cat file.txt | sed 's/_[0-9]*-[0-9]*-[0-9]*\.out$//'
EXAMPLE_FILE
EXAMPLE_FILE_2
$
答案 1 :(得分:1)
无需使用猫,昂贵的叉子和管道。 shell可以很好地剪切字符串:
$ file=EXAMPLE_FILE_2_2017-10-12.out
$ echo ${file%%_????-??-??.out}
EXAMPLE_FILE_2
阅读有关如何在友好的shell手册中使用%%,%,##和#运算符的所有内容。
答案 2 :(得分:0)
^\w+(?=_)
EXAMPLE_FILE_2017-09-12.out
EXAMPLE_FILE_2_2017-10-12.out
EXAMPLE_FILE
EXAMPLE_FILE_2
^
在行开头处断言位置\w+
在1和无限次之间匹配任何单词字符(a-zA-Z0-9_
)(?=_)
确保以下内容的正向前瞻是下划线_
字符答案 3 :(得分:0)
只需 sed :
sed 's/_[^_]*$//' file
输出:
EXAMPLE_FILE
EXAMPLE_FILE_2
<强> ---------- 强>
如果要遍历扩展名为.out
的文件列表 - bash 解决方案:
for f in *.out; do echo "${f%_*}"; done
答案 4 :(得分:0)
Bash本身具有正则表达式功能,因此您无需运行实用程序。例如:
for fn in *.out; do
[[ $fn =~ ^(.*)_[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} ]]
cap="${BASH_REMATCH[1]}"
printf "%s => %s\n" "$fn" "$cap"
done
使用示例文件,输出为:
EXAMPLE_FILE_2017-09-12.out => EXAMPLE_FILE
EXAMPLE_FILE_2_2017-10-12.out => EXAMPLE_FILE_2
使用Bash本身比为每个文件名生成sed
,awk
等更快,更有效。
当然在使用中,您需要测试成功匹配:
for fn in *.out; do
if [[ $fn =~ ^(.*)_[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} ]]; then
cap="${BASH_REMATCH[1]}"
printf "%s => %s\n" "$fn" "$cap"
else
echo "$fn no match"
fi
done
作为旁注,如果您只需要在文件名中的最后一个_
之后修剪字符串,则可以使用Bash parameter expansion而不是正则表达式:
for fn in *.out; do
cap="${fn%_*}"
printf "%s => %s\n" "$fn" "$cap"
done
然后针对$cap
测试$fn
。如果它们相等,则参数扩展不会在_
之后修剪文件名,因为它不存在。
正则表达式允许测试类似日期的字符串\d\d\d\d-\d\d-\d\d
在_
之后。由您决定。
答案 5 :(得分:0)
awk -F_ 'NF-=1' OFS=_ file
EXAMPLE_FILE
EXAMPLE_FILE_2
答案 6 :(得分:0)
你能不能尝试awk解决方案,它将处理所有.out文件,请注意这已经在GNU awk中编写和测试。
awk --re-interval 'FNR==1{if(val){close(val)};split(FILENAME, array,"_[0-9]{4}-[0-9]{2}-[0-9]{2}");print array[1];val=FILENAME;nextfile}' *.out
另外我的awk版本很旧,所以我使用的是--re-interval,如果你有最新版本的awk,那么你可能不需要使用它。
解释和解决方案的非单一内容:此处还添加了非单一的内联形式的解决方案。
awk --re-interval '##Using --re-interval for supporting ERE in my OLD awk version, if OP has new version of awk it could be removed.
FNR==1{ ##Checking here condition that when very first line of any Input_file is being read then do following actions.
if(val){ ##Checking here if variable named val value is NOT NULL then do following.
close(val) ##close the Input_file named which is stored in variable val, so that we will NOT face problem of TOO MANY FILES OPENED, so it will be like one file read close it in background then.
};
split(FILENAME, array,"_[0-9]{4}-[0-9]{2}-[0-9]{2}");##Splitting FILENAME(which will have Input_file name in it) into array named array only, whose separator is a 4 digits-2 digits- then 2 digits, actually this will take care of YYYY-MM-DD format in Input_file(s) and it will be easier for us to get the file name part.
print array[1]; ##Printing array 1st element here.
val=FILENAME; ##Storing FILENAME variable value which will have current Input_file name in it to variable named val, so that we could close it in background.
nextfile ##nextfile as it name suggests it will skip all the lines in current line and jump onto the next file to save some cpu cycles of our system.
}
' *.out ##Mentioning all *.out Input_file(s) here.