在日期Bash shellscript之前提取文件名

时间:2017-09-27 18:53:32

标签: regex linux bash shell

我正在尝试提取文件名的一部分 - 日期和后缀之前的所有内容。我不确定在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

7 个答案:

答案 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)

代码

See this code in use here

^\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本身比为每个文件名生成sedawk等更快,更有效。

当然在使用中,您需要测试成功匹配:

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.