在bash中,文件操作符(-f)可以不区分大小写吗?

发布于 2024-09-09 02:54:21 字数 519 浏览 0 评论 0原文

我正在执行以下操作:

if [ -f $FILE ] ; then
    echo "File exists"
fi

但我希望 -f 不区分大小写。也就是说,如果 FILE 是 /etc/somefile,我希望 -f 识别 /Etc/SomeFile

我可以使用 glob: 部分解决它,

shopt -s nocaseglob
TARG='/etc/somefile'

MATCH=$TARG*    #assume it returns only one match
 
if [[ -f $MATCH ]] ; then
    echo "File exists" 
fi

但不区分大小写的 globbing 仅适用于文件名部分,而不适用于完整路径。所以如果 TARG 是 /Etc/somefile ,它将不起作用。

有什么办法可以做到这一点吗?

I'm doing the following:

if [ -f $FILE ] ; then
    echo "File exists"
fi

But I want the -f to be case-insensitive. That is, if FILE is /etc/somefile, I want -f to recognize /Etc/SomeFile.

I can partially work around it with glob:

shopt -s nocaseglob
TARG='/etc/somefile'

MATCH=$TARG*    #assume it returns only one match
 
if [[ -f $MATCH ]] ; then
    echo "File exists" 
fi

but the case-insensitive globbing works only on the filename portion, not the full path. So it will not work if TARG is /Etc/somefile.

Is there any way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

旧情别恋 2024-09-16 02:54:21

问题是您的文件系统区分大小写。文件系统仅提供两种相关的方式来获取文件:要么指定一个精确的、区分大小写的文件名并以这种方式检查其是否存在,要么读取目录中的所有文件,然后检查每个文件是否与某种模式匹配。

换句话说,检查区分大小写的文件系统上是否存在不区分大小写的文件版本是非常低效的。 shell 可能会为您做这件事,但在内部它会读取所有目录的内容并根据模式检查每个内容。

考虑到所有这些,这是可行的:

if [[ -n $(find /etc -maxdepth 1 -iname passwd) ]]; then
  echo "Found";
fi

但是除非您想搜索从“/”开始的所有内容,否则您必须单独检查路径的组成部分。这是没有办法解决的;您无法神奇地检查区分大小写的文件系统上不区分大小写的匹配的整个路径!

The problem is that your filesystem is case-sensitive. The filesystem provides only two relevant ways to get a file: either you specify an exact, case-sensitive filename and check for its existence that way, or you read all the files in a directory and then check if each one matches a pattern.

In other words, it is very inefficient to check if a case-insensitive version of a file exists on a case-sensitive filesystem. The shell might do it for you, but internally it's reading all the directory's contents and checking each one against a pattern.

Given all that, this works:

if [[ -n $(find /etc -maxdepth 1 -iname passwd) ]]; then
  echo "Found";
fi

BUTunless you want to search everything from '/' on down, you must check component of the path individually. There is no way around this; you can't magically check a whole path for case-insensitive matches on a case-sensitive filesystem!

甩你一脸翔 2024-09-16 02:54:21

您可以使用 nocasematch 选项

shopt -s nocasematch
for file in *
do
  case "$file" in
   "/etc/passWd" ) echo $file;;
  esac
done 

you can use the nocasematch option

shopt -s nocasematch
for file in *
do
  case "$file" in
   "/etc/passWd" ) echo $file;;
  esac
done 
偏爱自由 2024-09-16 02:54:21

不知道如何只使用 shell 评估。
但 grep 可以不区分大小写,因此调用 grep 、 find 和 wc 的脚本可能会满足您的需求。

not knowing howto only using shell evaluations.
but grep can be case-insensitive, therefore a script that invokes grep , find and wc may meet your demand.

表情可笑 2024-09-16 02:54:21

一般来说,如果文件系统区分大小写,这是很难做到的。基本上,您已经分别迭代每个祖先目录。这是 Python 的起点:

import os

def exists_nocase(path):
    if os.path.exists(path):
        return True
    path = os.path.normpath(os.path.realpath(os.path.abspath(unicode(path)))).upper()
    parts = path.split(os.sep)
    path = unicode(os.path.join(unicode(os.path.splitdrive(path)[0]), os.sep))
    for name in parts:
        if not name:
            continue
        # this is a naive and insane way to do case-insensitive string comparisons:
        entries = dict((entry.upper(), entry) for entry in os.listdir(path))
        if name in entries:
            path = os.path.join(path, entries[name])
        else:
            return False
    return True

print exists_nocase("/ETC/ANYTHING")
print exists_nocase("/ETC/PASSWD")

This is very hard to do, in general, if the file system is case-sensitive. Basically, you have iterate over each of the ancestor directories separately. Here is a starting point in Python:

import os

def exists_nocase(path):
    if os.path.exists(path):
        return True
    path = os.path.normpath(os.path.realpath(os.path.abspath(unicode(path)))).upper()
    parts = path.split(os.sep)
    path = unicode(os.path.join(unicode(os.path.splitdrive(path)[0]), os.sep))
    for name in parts:
        if not name:
            continue
        # this is a naive and insane way to do case-insensitive string comparisons:
        entries = dict((entry.upper(), entry) for entry in os.listdir(path))
        if name in entries:
            path = os.path.join(path, entries[name])
        else:
            return False
    return True

print exists_nocase("/ETC/ANYTHING")
print exists_nocase("/ETC/PASSWD")
你爱我像她 2024-09-16 02:54:21

因为这个问题被标记为

简介

我在临时挂载点挂载 NTFS 分区后搜索 Windows 的注册表文件 mnt=$(mktemp -d)

然后,如果命令:

regfile=$(find $mnt -iwholename "$mnt/window*/system32/config/software")

执行该工作,因为该命令基于 $mnt,find 将愚蠢地扫描整个文件系统并花费大量时间

我的纯bash函数

为此,我编写了这个ipath函数:

ipath() {
    if [[ $1 == -v ]]; then local -n _res=$2; shift 2; else local _res; fi
    local _path _i _chr
    _res=
    for ((_i=0;_i<${#1};_i++));do
        _chr=${1:_i:1} _chr=${_chr,}
        [[ $_chr != ${_chr^} ]] && _path+="[$_chr${_chr^}]" ||
                _path+=$_chr
    done
    _path=($_path)
    [[ -e ${_path[0]} ]] || return 1
    if [[ ${_res@A} == _res=* ]] ;then
        printf "%s\n" "${_path[@]}"
    else
        _res=("${_path[@]}")
    fi
}

然后用于测试:

$ ipath "$mnt/window*/system32/config/software"
/tmp/tmp.Q2czvPDs5m/Windows/System32/config/SOFTWARE

$ ipath ~/desktop
/home/user/Desktop

用作变量

$ ipath -v regfile "$mnt/window*/system32/config/software"
$ echo $regfile 
/tmp/tmp.Q2czvPDs5m/Windows/System32/config/SOFTWARE

$ ipath -v DeskDir ~/desktop
echo $DeskDir
/home/user/Desktop

另一个具有多个答案的测试和空格路径:

$ ipath $mnt/program\*/int\*er
/tmp/tmp.jVs5mPGbJm/Program Files/Internet Explorer
/tmp/tmp.jVs5mPGbJm/Program Files (x86)/Internet Explorer

$ ipath -v iedirs "$mnt/program*/int*er"
$ declare -p iedirs
declare -a iedirs=([0]="/tmp/tmp.jVs5mPGbJm/Program Files/Internet Explorer" [1]="/tmp/tmp.jVs5mPGbJm/Program Files (x86)/Internet Explorer")

注意,使用 unicode 重音字符进行测试: résumé.pdf 变为 [rR][éÉ][sS][uU][mM][éÉ].[pP][dD] [fF]

As this question is tagged

Introduction

I was searching for window's registry file after mounted NTFS partition in a temporary mount point mnt=$(mktemp -d)

Then, if the command:

regfile=$(find $mnt -iwholename "$mnt/window*/system32/config/software")

do the job, as this command is based on $mnt, find will stupidely scan the whole filesystem and take a lot of time.

My pure bash function

For this, I wrote this ipath function:

ipath() {
    if [[ $1 == -v ]]; then local -n _res=$2; shift 2; else local _res; fi
    local _path _i _chr
    _res=
    for ((_i=0;_i<${#1};_i++));do
        _chr=${1:_i:1} _chr=${_chr,}
        [[ $_chr != ${_chr^} ]] && _path+="[$_chr${_chr^}]" ||
                _path+=$_chr
    done
    _path=($_path)
    [[ -e ${_path[0]} ]] || return 1
    if [[ ${_res@A} == _res=* ]] ;then
        printf "%s\n" "${_path[@]}"
    else
        _res=("${_path[@]}")
    fi
}

Then for testing:

$ ipath "$mnt/window*/system32/config/software"
/tmp/tmp.Q2czvPDs5m/Windows/System32/config/SOFTWARE

$ ipath ~/desktop
/home/user/Desktop

for use as a variable:

$ ipath -v regfile "$mnt/window*/system32/config/software"
$ echo $regfile 
/tmp/tmp.Q2czvPDs5m/Windows/System32/config/SOFTWARE

$ ipath -v DeskDir ~/desktop
echo $DeskDir
/home/user/Desktop

Another test with multiple answer and spaced path:

$ ipath $mnt/program\*/int\*er
/tmp/tmp.jVs5mPGbJm/Program Files/Internet Explorer
/tmp/tmp.jVs5mPGbJm/Program Files (x86)/Internet Explorer

$ ipath -v iedirs "$mnt/program*/int*er"
$ declare -p iedirs
declare -a iedirs=([0]="/tmp/tmp.jVs5mPGbJm/Program Files/Internet Explorer" [1]="/tmp/tmp.jVs5mPGbJm/Program Files (x86)/Internet Explorer")

Note, tested with unicode accented characters: résumé.pdf become [rR][éÉ][sS][uU][mM][éÉ].[pP][dD][fF].

属性 2024-09-16 02:54:21
cd /etc
# using FreeBSD find
find -x -L "$(pwd)" -maxdepth 1 -type f -iregex "/EtC/[^\/]*" -iname paSSwd 
cd /etc
# using FreeBSD find
find -x -L "$(pwd)" -maxdepth 1 -type f -iregex "/EtC/[^\/]*" -iname paSSwd 
心凉 2024-09-16 02:54:21

这是使用 Bash 的起点:

#  fci -- check if file_case_insensitive exists 
# (on a case sensitive file system; complete file paths only!)

function fci() {   

declare IFS checkdirs countslashes dirpath dirs dirstmp filepath fulldirpaths i name ndirs result resulttmp

[[ -f "$1" ]] && { return 0; }
[[ "${1:0:1}" != '/' ]] && { echo "No absolute file path given: ${1}" 2>&1; return 1; }
[[ "$1" == '/' ]] && { return 1; }

filepath="$1"
filepath="${filepath%"${filepath##*[!/]}"}"  # remove trailing slashes, if any
dirpath="${filepath%/*}"
name="${filepath##*/}"

IFS='/'
dirs=( ${dirpath} )

if [[ ${#dirs[@]} -eq 0 ]]; then
   fulldirpaths=( '/' )
   ndirs=1
else
   IFS=""
   dirs=( ${dirs[@]} )
   ndirs=${#dirs[@]}

   for ((i=0; i < ${ndirs}; i++)); do

      if [[ $i -eq 0 ]]; then
         checkdirs=( '/' )
      else
         checkdirs=( "${dirstmp[@]}" )
      fi

      IFS=
\777'
      dirstmp=( $( find -x -L "${checkdirs[@]}" -mindepth 1 -maxdepth 1 -type d -iname "${dirs[i]}" -print0 2>/dev/null | tr '\0' '\777' ) )

      IFS=""
      fulldirpaths=( ${fulldirpaths[@]} ${dirstmp[@]} )

   done

fi

printf "fulldirpaths: %s\n" "${fulldirpaths[@]}" | nl

for ((i=0; i < ${#fulldirpaths[@]}; i++)); do
   countslashes="${fulldirpaths[i]//[^\/]/}"
   [[ ${#countslashes} -ne ${ndirs} ]] && continue
   IFS=
\777'
   resulttmp=( $( find -x -L "${fulldirpaths[i]}" -mindepth 1 -maxdepth 1 -type f -iname "${name}" -print0 2>/dev/null | tr '\0' '\777' ) )
   IFS=""
   result=( ${result[@]} ${resulttmp[@]} )
done

IFS=""
result=( ${result[@]} )

printf "result: %s\n" "${result[@]}" | nl

if [[ ${#result[@]} -eq 0 ]]; then
   return 1
else
   return 0
fi
}


FILE='/eTC/paSSwD'

if fci "${FILE}" ; then
   echo "File (case insensitive) exists: ${FILE}" 
else
   echo "File (case insensitive) does NOT exist: ${FILE}" 
fi

And here's a starting point using Bash:

#  fci -- check if file_case_insensitive exists 
# (on a case sensitive file system; complete file paths only!)

function fci() {   

declare IFS checkdirs countslashes dirpath dirs dirstmp filepath fulldirpaths i name ndirs result resulttmp

[[ -f "$1" ]] && { return 0; }
[[ "${1:0:1}" != '/' ]] && { echo "No absolute file path given: ${1}" 2>&1; return 1; }
[[ "$1" == '/' ]] && { return 1; }

filepath="$1"
filepath="${filepath%"${filepath##*[!/]}"}"  # remove trailing slashes, if any
dirpath="${filepath%/*}"
name="${filepath##*/}"

IFS='/'
dirs=( ${dirpath} )

if [[ ${#dirs[@]} -eq 0 ]]; then
   fulldirpaths=( '/' )
   ndirs=1
else
   IFS=""
   dirs=( ${dirs[@]} )
   ndirs=${#dirs[@]}

   for ((i=0; i < ${ndirs}; i++)); do

      if [[ $i -eq 0 ]]; then
         checkdirs=( '/' )
      else
         checkdirs=( "${dirstmp[@]}" )
      fi

      IFS=
\777'
      dirstmp=( $( find -x -L "${checkdirs[@]}" -mindepth 1 -maxdepth 1 -type d -iname "${dirs[i]}" -print0 2>/dev/null | tr '\0' '\777' ) )

      IFS=""
      fulldirpaths=( ${fulldirpaths[@]} ${dirstmp[@]} )

   done

fi

printf "fulldirpaths: %s\n" "${fulldirpaths[@]}" | nl

for ((i=0; i < ${#fulldirpaths[@]}; i++)); do
   countslashes="${fulldirpaths[i]//[^\/]/}"
   [[ ${#countslashes} -ne ${ndirs} ]] && continue
   IFS=
\777'
   resulttmp=( $( find -x -L "${fulldirpaths[i]}" -mindepth 1 -maxdepth 1 -type f -iname "${name}" -print0 2>/dev/null | tr '\0' '\777' ) )
   IFS=""
   result=( ${result[@]} ${resulttmp[@]} )
done

IFS=""
result=( ${result[@]} )

printf "result: %s\n" "${result[@]}" | nl

if [[ ${#result[@]} -eq 0 ]]; then
   return 1
else
   return 0
fi
}


FILE='/eTC/paSSwD'

if fci "${FILE}" ; then
   echo "File (case insensitive) exists: ${FILE}" 
else
   echo "File (case insensitive) does NOT exist: ${FILE}" 
fi
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文