Bash脚本:处理文件名和输出文件名中带有空格的文件

时间:2018-10-12 05:11:06

标签: bash

我需要编写一个实现以下目标的Bash脚本:

1)将最新的pdf文件从文件夹1移至文件夹2;

2)正确处理文件名中可能带有空格的文件;

3)在文本文件中的特定位置输出每个文件名。 (在我的实际用法中,我将使用sed将文件名放在现有文件的特定位置。)

我试图制作一个文件名数组,然后将它们移动并循环输出文本。但是,以下数组无法处理文件名中带有空格的文件:

pdfs=($(find -name "$DOWNLOADS/*.pdf" -print0 | xargs -0 ls -1 -t | head -n$NUM))

假设文件的名称为“带空格的文件名”。我从上面的数组中获得的内容将在单独的数组条目中包含“ with”和“ Space”。 我不确定如何避免将相同文件名中的这些单词分开对待。 有人可以帮我吗?

谢谢!

-------------更新------------

抱歉,我对第三点含糊不清,因为我认为在实现第一个和第二个目标之后我也许能弄清楚这一点。

基本上,这是一个文本文件,其结尾处以“%comment”开头,并且我需要以“ file = PATH”的格式在该行之前插入文件名。 PATH是我将pdf移至的文件夹2。

4 个答案:

答案 0 :(得分:1)

您可以结合使用mapfilegnu的{​​{1}}版本的find | sort | cut | head版本来实现此目的,这些版本可以对NUL终止的文件名进行操作:

mapfile -d '' -t pdfs < <(find "$DOWNLOADS/*.pdf" -name 'file*' -printf '%T@:%p\0' | 
sort -z -t : -rnk1 | cut -z -d : -f2- | head -z -n $NUM)

使用的命令是:

  1. mapfile -d '':使用NUL作为分隔符读取数组
  2. find:以EPOCH +“:” +文件名+ NUL字节的形式输出每个文件的修改标记
  3. sort:在第一个字段上按数字反向排序
  4. cut:从输出中删除第一个字段
  5. head:仅输出前$ NUM个文件名

答案 1 :(得分:0)

我想下面的代码将接近您想要的代码:

IFS=$'\n' pdfs=($(find -name "$DOWNLOADS/*.pdf" -print0 | xargs -0 -I ls -lt "{}" | tail -n +1 | head -n$NUM))

然后,您可以通过${pdfs[0]}${pdfs[1]},...

访问输出

说明

  • IFS=$'\n'仅将以下行用“ \ n”分隔。
  • -I
  • xargs选项告诉xargs将{}替换为文件名,以便将其引用为"{}"
  • tail -n +1是一种抑制显示“ xargs:'ls'被信号13终止”错误消息的技巧。

希望这会有所帮助。

答案 2 :(得分:0)

Bash v4有一个选项globstar,启用此选项后,我们可以使用**来匹配零个或多个子目录。

mapfile是一个内置命令,用于将行读入索引数组变量。 -t选项删除尾随换行符。

shopt -s globstar
mapfile -t pdffiles < <(ls -t1 **/*.pdf | head -n"$NUM")

typeset -p pdffiles

for f in "${pdffiles[@]}"; do
  echo "==="
  mv "${f}" /dest/path
  sed "/^%comment/i${f}=/dest/path" a-text-file.txt
done

答案 3 :(得分:0)

find downloads -name "*.pdf" -printf "%T@ %p\0" |
sort -z -t' ' -k1 -n |
cut -z -d' ' -f2- |
tail -z -n 3
  1. 在下载中找到所有* .pdf文件
  2. 对于每个文件打印,其修改日期为%T,格式说明符为@,这意味着自纪元以来以秒为单位的小数部分,然后打印空间,文件名并用\0分隔
  3. 使用空格作为字段分隔符对空分隔的流进行排序,仅使用数值排序仅使用第一个字段
  4. 从流中删除第一个字段,即创建日期,仅保留文件名。
  5. 使用tail获取最新文件的计数,在此示例中为3个最新文件。我们还可以进行反向排序并使用head,没有区别。
  6. 请勿在脚本中使用lsls用于获得格式良好的输出。您可以执行xargs -0 stat --printf "%Y %n\0",这基本上可以使脚本向前移动,因为ls并不打算用于脚本。只是我无法将stat的输出分数作为创建日期的一部分。

对于第二部分,我们需要将空分界列表保存到文件中

find downloads ........ >"$tmp"

然后:

str='%comment'
{
    grep -B$((2**32)) -x "$str" "$out" | grep -v "$str"
    # I don't know what you expect to do with newlines in filenames, but I guess you don't have those
    cat "$tmp" | sed -z 's/^/file=/' | sed 's/\x0/\n/g'
    grep -A$((2**32)) -x "$str" "$out"
} | sponge "$out"

其中output是输出文件名

  1. 假设输出文件名存储在变量"$out"
  2. 过滤%comment之前的所有行,并从文件中删除行%comment本身
  3. 在每个文件名的开头都带有file=。我还用零代替了换行符。
  4. 过滤%comment之后的所有行,包括%comment本身
  5. 为outfile编写输出。记住要使用一个临时文件。
  6. 请勿在空分隔的输入上使用pdf=$(...)。您可以使用mapfile将其存储到数组中,就像提供其他答案一样。

然后移动文件,做类似的事情

<"$tmp" xargs -0 -i mv {} "$outdir"

或更快,只需一步:

{ cat <"$tmp"; printf "%s\0" "$outdir"; } | xargs -0 mv

或者:

<"$tmp" xargs -0 sh -c 'outdir="$1"; shift; mv "$@" "$outdir"' -- "$outdir"

turorialspoint上的实时示例。