创建新文件,但如果文件名已存在于bash中,则添加数字

hel*_*ker 24 linux bash filenames

我发现了类似的问题,但在Linux/Bash中没有

我希望我的脚本创建一个具有给定名称的文件(通过用户输入),但如果filename已经存在,则在末尾添加数字.

例:

$ create somefile
Created "somefile.ext"
$ create somefile
Created "somefile-2.ext"
Run Code Online (Sandbox Code Playgroud)

cho*_*oba 32

以下脚本可以帮助您.您不应该同时运行脚本的多个副本以避免竞争条件.

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
Run Code Online (Sandbox Code Playgroud)

  • 这里有一些编码实践的问题.如果somefile.ext是不存在的文件(或某个不可访问的目录中的文件)的符号链接,如果$ IFS包含"e",如果你想将它改编为name =" - some file--"该怎么办? ? (7认同)
  • 它工作,但我将初始`i`更改为2,以便成为文件的"第二"副本.谢谢! (2认同)

Hug*_*ako 8

更轻松:

touch file`ls file* | wc -l`.ext
Run Code Online (Sandbox Code Playgroud)

你会得到:

$ ls file*
file0.ext  file1.ext  file2.ext  file3.ext  file4.ext  file5.ext  file6.ext
Run Code Online (Sandbox Code Playgroud)

  • 很好,如果(如果有的话)您可以确定那里永远不会有不连续的数字,即,如果我在示例中删除过“ file2.txt”,那么现在将破坏“ file6.txt”。 (2认同)

Ste*_*las 6

为避免竞争条件:

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"\n' "$file"
echo some text in it >&3
Run Code Online (Sandbox Code Playgroud)

此外,您还可以在fd 3上打开文件.

有了bash-4.4+,你可以使它像这样的功能:

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
}
Run Code Online (Sandbox Code Playgroud)

例如用作:

create 3 somefile .ext || exit
printf 'File: "%s"\n' "$REPLY"
echo something >&3
Run Code Online (Sandbox Code Playgroud)

max当由于其他原因无法创建文件时,该值可用于防止无限循环noclobber.

需要注意的是noclobber仅适用于>运营商,不是>>也没有<>.

剩余的竞争条件

实际上,noclobber在所有情况下都没有消除竞争条件.它只能防止破坏常规文件(不是其他类型的文件,因此cmd > /dev/null例如不会失败)并且在大多数shell中都有竞争条件.

shell首先stat(2)在文件上检查它是否是常规文件(fifo,目录,设备......).仅当文件不存在(尚未)或是常规文件时,才3> "$file"使用O_EXCL标志来保证不破坏文件.

因此,如果有一个名称的fifo或设备文件,它将被使用(前提是它可以以只写方式打开),如果创建一个常规文件作为fifo/device /目录的替代品,它可能会被破坏. ..在这之间stat(2)open(2)没有O_EXCL!

现在,面对恶意攻击者而言,这只是一个问题,它会让你覆盖文件系统上的任意文件.它确实消除了同时运行同一脚本的两个实例的正常情况下的竞争条件.因此,在这方面,它比预先检查文件存在的方法更好[ -e "$file" ].

对于没有竞争条件的工作版本,您可以使用zshshell而不是bash具有原始接口open()sysopenshell作为zsh/system模块中的内置:

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"\n' "$file"
echo some text in it >&3
Run Code Online (Sandbox Code Playgroud)


Gyu*_*hin 5

试试这个

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
Run Code Online (Sandbox Code Playgroud)


Ser*_*suk 5

使用touch或任何你想要的代替echo

echo file$((`ls file* | sed -n 's/file\([0-9]*\)/\1/p' | sort -rh | head -n 1`+1))
Run Code Online (Sandbox Code Playgroud)

部分表达解释:

  • 按模式列出文件: ls file*
  • 每行只取数字部分: sed -n 's/file\([0-9]*\)/\1/p'
  • 应用反向人类排序: sort -rh
  • 只取第一行(即最大值): head -n 1
  • 在管道和增量中组合所有内容(上面的完整表达式)