Create new file but add number if filename already exists in bash
我发现了类似的问题,但在Linux / Bash中没有
我希望我的脚本创建一个具有给定名称的文件(通过用户输入),但如果filename已经存在,则在末尾添加数字。
例:
1 2 3 4 | $ create somefile Created"somefile.ext" $ create somefile Created"somefile-2.ext" |
以下脚本可以帮助您。您不应该同时运行脚本的多个副本以避免竞争条件。
1 2 3 4 5 6 7 8 9 | name=somefile if [[ -e $name.ext ]] ; then i=0 while [[ -e $name-$i.ext ]] ; do let i++ done name=$name-$i fi touch"$name".ext |
更轻松:
1 | touch file`ls file* | wc -l`.ext |
你会得到:
1 2 | $ ls file* file0.ext file1.ext file2.ext file3.ext file4.ext file5.ext file6.ext |
为避免竞争条件 strike>:
1 2 3 4 5 6 7 8 9 10 11 12 13 | name=some-file n= set -o noclobber until file=$name${n:+-$n}.ext { command exec 3>"$file"; } 2> /dev/null do ((n++)) done printf 'File is"%s" '"$file" echo some text in it >&3 |
此外,您还可以在fd 3上打开文件。
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | create() { # fd base [suffix [max]]] local fd="$1" base="$2" suffix="${3-}" max="${4-}" local n= file local - # ash-style local scoping of options in 4.4+ set -o noclobber REPLY= until file=$base${n:+-$n}$suffix eval 'command exec '"$fd"'>"$file"' 2> /dev/null do ((n++)) ((max > 0 && n > max)) && return 1 done REPLY=$file } |
例如用作:
1 2 3 4 | create 3 somefile .ext || exit printf 'File:"%s" '"$REPLY" echo something >&3 |
当由于
请注意,
剩余的竞争条件
实际上,
shell首先对文件执行
因此,如果有一个名称的fifo或设备文件,它将被使用(前提是它可以以只写方式打开),如果创建一个常规文件作为fifo / device /目录的替代品,它可能会被破坏。 ...在没有O_EXCL的
现在,面对恶意攻击者而言,这只是一个问题,它会让你覆盖文件系统上的任意文件。它确实消除了同时运行同一脚本的两个实例的正常情况下的竞争条件。所以,在那里,它比预先用
对于没有竞争条件的工作版本,您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | zmodload zsh/system name=some-file n= until file=$name${n:+-$n}.ext sysopen -w -o excl -u 3 --"$file" 2> /dev/null do ((n++)) done printf 'File is"%s" '"$file" echo some text in it >&3 |
尝试这样的事情
1 2 3 4 5 6 7 8 9 10 11 12 13 | name=somefile path=$(dirname"$name") filename=$(basename"$name") extension="${filename##*.}" filename="${filename%.*}" if [[ -e $path/$filename.$extension ]] ; then i=2 while [[ -e $path/$filename-$i.$extension ]] ; do let i++ done filename=$filename-$i fi target=$path/$filename.$extension |
使用
1 | echo file$((`ls file* | sed -n 's/file\([0-9]*\)/\1/p' | sort -rh | head -n 1`+1)) |
部分表达解释:
-
按模式列出文件:
ls file* -
在每一行中仅取数字部分:
sed -n 's/file\([0-9]*\)/\1/p' -
应用反向人类排序:
sort -rh -
仅取第一行(即最大值):
head -n 1 - 将所有管道和增量组合在一起(上面的完整表达)
尝试这样的事情(未经测试,但你明白了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | filename=$1 # If file doesn't exist, create it if [[ ! -f $filename ]]; then touch $filename echo"Created "$filename"" exit 0 fi # If file already exists, find a similar filename that is not yet taken digit=1 while true; do temp_name=$filename-$digit if [[ ! -f $temp_name ]]; then touch $temp_name echo"Created "$temp_name"" exit 0 fi digit=$(($digit + 1)) done |
根据您正在执行的操作,将
这是我用于逐步创建目录的更好的方法。
它也可以调整文件名。
1 2 3 4 5 6 7 | LAST_SOLUTION=$(echo $(ls -d SOLUTION_[[:digit:]][[:digit:]][[:digit:]][[:digit:]] 2> /dev/null) | awk '{ print $(NF) }') if [ -n"$LAST_SOLUTION" ] ; then mkdir SOLUTION_$(printf"%04d " $(expr ${LAST_SOLUTION: -4} + 1)) else mkdir SOLUTION_0001 fi |
将choroba答案的简单重新包装作为一种通用功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | autoincr() { f="$1" ext="" # Extract the file extension (if any), with preceeding '.' [["$f" == *.* ]] && ext=".${f##*.}" if [[ -e"$f" ]] ; then i=1 f="${f%.*}"; while [[ -e"${f}_${i}${ext}" ]]; do let i++ done f="${f}_${i}${ext}" fi echo"$f" } touch"$(autoincr"somefile.ext")" |