linux bash创建文件,关于linux:创建新文件但是如果bash中已经存在filename,则添加数字...

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

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

例:

$ create somefile

Created"somefile.ext"

$ create somefile

Created"somefile-2.ext"

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

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

它工作,但我将初始i更改为2,以便成为文件的"第二"副本。谢谢!

这里有一些编码实践的问题。如果somefile.ext是不存在的文件(或某个不可访问的目录中的文件)的符号链接,如果$ IFS包含"e",如果你想将它改编为name =" - some file--"该怎么办? ?

我在循环中第一次使用了else条件

如果列表可能会增长到很多文件,就不会使用它,因为它会继续变慢

更轻松:

touch file`ls file* | wc -l`.ext

你会得到:

$ ls file*

file0.ext  file1.ext  file2.ext  file3.ext  file4.ext  file5.ext  file6.ext

如果我希望第一个输出是file1.ext,它将如何工作?

很好,如果(很大的话)你确定你永远不会有非连续数字,即如果我在你的例子中删除file2.txt,我现在将破坏file6.txt。

通常你应该避免在脚本中使用ls,但这似乎是无害的。对于样式点,将ls替换为printf '%s

'(或者甚至printf '.

',它对文件名具有强健性,并带有换行符)。

为避免竞争条件 strike>:

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上打开文件。

使用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

}

例如用作:

create 3 somefile .ext || exit

printf 'File:"%s"

'"$REPLY"

echo something >&3

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

请注意,noclobber仅适用于>运算符,不适用于>>或<>。

剩余的竞争条件

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

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

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

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

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

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

尝试这样的事情

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

这应该是公认的答案,因为它确实处理了所有事情,包括在扩展之前添加后缀!

@sorin,对于以-开头的$name值,它仍然失败。在//file特殊的系统上,name的值可能会失败,如/file。它不能防止$name被破坏的符号链接。并且有一个TOCTOU竞争条件(注意它在zsh中断,其中$path是绑定到$path的特殊数组)

将上述内容复制到backup.sh:$ ./backup.sh blah.txt时,以下命令不执行任何操作。我希望有一个名为"blah2.txt"的新文件或其他类似文件。

使用touch或任何你想要的而不是echo:

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

将所有管道和增量组合在一起(上面的完整表达)

好一个班轮,谢谢!

尝试这样的事情(未经测试,但你明白了):

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

根据您正在执行的操作,将touch的调用替换为创建您正在使用的文件所需的任何代码。

这是我用于逐步创建目录的更好的方法。

它也可以调整文件名。

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

这有许多不幸的反模式,包括无用的echo和使用大写的私有变量,这些变量是为系统使用而保留的。

将choroba答案的简单重新包装作为一种通用功能:

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")"

正如@ Choroba的答案[[ -e .... ]]已经评论stat(2),您需要[[ -e ... || -L ... ]]来搜索文件是否存在,但即使这样也存在竞争条件,最好是使用O_EXCL为系统打开文件以保证您'不使用现有文件(或更糟糕的是敏感文件的符号链接)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值