重命名每个组中的最新文件

时间:2017-11-19 13:19:13

标签: linux bash shell

我尝试创建一个应该检测每个组的最新文件的脚本,并在其原始名称中添加前缀。

ll $DIR
asset_10.0.0.1_2017.11.19 #latest
asset_10.0.0.1_2017.10.28
asset_10.0.0.2_2017.10.02 #latest
asset_10.0.0.2_2017.08.15
asset_10.1.0.1_2017.11.10 #latest
...

2个问题:

1)如何找到每组的最新文件?

2)如何重命名只添加前缀

我尝试了以下过程,但它在整个目录中查找最新文件,并且不保留原始名称以向其添加前缀:

find $DIR -type f ! -name 'asset*' -print | sort -n | tail -n 1 | xargs -I '{}' cp -p '{}' $DIR...

实现这一目标的最佳方法是什么? (如果可能,保持xargs)

2 个答案:

答案 0 :(得分:5)

选择每组中的最新条目

您可以使用sort仅选择每个组中的最新条目:

find . -print0 | sort -r -z | sort -t_ -k2,2 -u -z | xargs ...

首先,按逆向字典顺序对所有文件进行排序(以便最新条目首先显示每个组)。然后,通过仅对组名进行排序(当通过-k2,2在下划线上拆分时,该第二个字段-t_)并打印唯一的组,我们每个组只获得第一个条目,这也是最新。

请注意,这是有效的,因为sort使用稳定排序算法 - 这意味着订单或已排序的项目不会通过再次排序来更改。另请注意,我们无法在此使用uniq,因为我们无法为uniq指定自定义字段分隔符(它始终是空格)。

使用前缀

进行复制

要为找到的每个文件名添加前缀,我们需要将每个路径 find生成分割为目录和文件名(basename),因为我们需要添加prefix仅限文件名。上面的xargs部分可能如下所示:

... | xargs -0 -I '{}' sh -c 'd="${1%/*}"; f="${1##*/}"; cp -p "$d/$f" "$d/prefix_$f"' _ '{}'

路径拆分使用shell parameter expansion完成,即前缀(${1##*/})和后缀(${1%/*}substring removal

请注意NUL中使用find终止的输出(路径)(-print0而不是-print),以及随后使用的-z sort中的-0xargs。这样,完整的管道将使用" special"正确处理文件名(路径)。像换行符和类似的字符。

答案 1 :(得分:2)

如果您想单独使用bash执行此操作,而不是使用findsort等外部工具,则需要解析"字段"在每个文件名中。

这样的事可能有用:

declare -A o=()                         # declare an associative array (req bash 4)

for f in asset_*; do                    # step through the list of files,
  IFS=_ read -a a <<<"$f"               # assign filename elements to an array
  b="${a[0]}_${a[1]}"                   # define a "base" of the first two elements
  if [[ "${a[2]}" > "${o[$b]}" ]]; then # compare the date with the last value
    o[$b]="${a[2]}"                     # for this base and reassign if needed
  fi
done

for i in "${!o[@]}"; do                 # now that we're done, step through results
  printf "%s_%s\n" "$i" "${o[$i]}"      # and print them.
done

这并不完全排序,它只是浏览文件列表并获取每个文件名库的最高排序值。