Shスクリプトの変数をファイルのベース名の最後の3文字に設定しようとしています(ベース名とは、パスなしおよびサフィックスなし) 。私はこれに成功しましたが、純粋に好奇心から、私が使用できるより短い単一のコマンドがあるかどうか疑問に思っています。元々私はawk
のワンライナーを持っていましたが、それはかなり長かったです。現在、私はこの2行のスクリプトを持っています(完全なファイル名が$1
にあると仮定):
filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
したがって、たとえば、 "/ path/to/somefile.txt"は$lastpart
の "ile"で終わります。
どういうわけかbasename
とビットを組み合わせてサフィックスを単一のコマンドにストリップできますか?パイプを使用せずにそれをtail
(または私が使用できる何か)に送信する方法はありますか?接尾辞は不明なので、basename
のパラメータとして使用することはできません。
主な目標は、実際にはできるだけ短くして一目で読めるようにすることではありません。これらすべての実際のコンテキストは スーパーユーザーに関するこの質問 です。ここでは、かなり単純な答えを考え出そうとしています。
これはexpr
の典型的な仕事です:
_$ file=/path/to/abcdef.txt
$ expr "/$file" : '.*\([^/.]\{3\}\)\.[^/.]*$'
def
_
ファイル名が予想される形式(ドットが1つだけで、ドットの前に少なくとも3文字が含まれている)であることがわかっている場合は、次のように簡略化できます。
_expr "/$file" : '.*\(.\{3\}\)\.'
_
一致がない場合、および一致した部分が0に解決される数値である場合、終了ステータスはゼロ以外になることに注意してください。(_a000.txt
_または_a-00.txt
_など)
zsh
の場合:
_file=/path/to/abcdef.txt
lastpart=${${file:t:r}[-3,-1]}
_
(_:t
_ for tail(basename)、_:r
_ for rest(extensionを削除した場合)).
var=123456
echo "${var#"${var%???}"}"
###OUTPUT###
456
最初に$var
から最後の3文字を削除し、次に$var
からその削除の結果を削除します。これにより、$var
の最後の3文字が返されます。以下は、そのようなことをどのように行うかを示すことを具体的に目的としたいくつかの例です。
touch file.txt
path=${PWD}/file.txt
echo "$path"
/tmp/file.txt
base=${path##*/}
exten=${base#"${base%???}"}
base=${base%."$exten"}
{
echo "$base"
echo "$exten"
echo "${base}.${exten}"
echo "$path"
}
file
txt
file.txt
/tmp/file.txt
それほど多くのコマンドでこれをすべて広げる必要はありません。これを圧縮できます:
{
base=${path##*/} exten=
printf %s\\n "${base%.*}" "${exten:=${base#"${base%???}"}}" "$base" "$path"
echo "$exten"
}
file
txt
file.txt
/tmp/file.txt
txt
$IFS
をset
tingと組み合わせることも、シェル変数を解析してドリルする非常に効果的な方法です。
(IFS=. ; set -f; set -- ${path##*/}; printf %s "${1#"${1%???}"}")
これにより、/
の最後の$path
に続く最初のピリオドの直前の3文字のみが取得されます。 .
の最後の$path
の直前の最初の3文字のみを取得する場合(たとえば、ファイル名に複数の.
が含まれている可能性がある場合):
(IFS=.; set -f; set -- ${path##*/}; ${3+shift $(($#-2))}; printf %s "${1#"${1%???}"}")
どちらの場合でも、次のことができます。
newvar=$(IFS...)
そして...
(IFS...;printf %s "$2")
....
に続くものを出力します
外部プログラムを使用してもかまわない場合は、次のことができます。
printf %s "${path##*/}" | sed 's/.*\(...\)\..*/\1/'
ファイル名に\n
ewline文字が含まれる可能性がある場合(ネイティブシェルソリューションには適用されません-とにかくすべて処理されます):
printf %s "${path##*/}" | sed 'H;$!d;g;s/.*\(...\)\..*/\1/'
Perl
を使用できる場合:
lastpart=$(
Perl -e 'print substr((split(/\.[^.]*$/,shift))[0], -3, 3)
' -- "$(basename -- "$1")"
)
sed
はこれで動作します:
[user@Host ~]$ echo one.two.txt | sed -r 's|(.*)\..*$|\1|;s|.*(...)$|\1|'
two
または
[user@Host ~]$ sed -r 's|(.*)\..*$|\1|;s|.*(...)$|\1|' <<<one.two.txt
two
sed
が-r
をサポートしていない場合は、()
のインスタンスを\(
および\)
に置き換えるだけで、-r
は必要です。
Perlが利用可能な場合、他のソリューションより読みやすくなる可能性があります。特に、正規表現言語の方が表現力があり、/x
修飾子。これにより、より明確な正規表現を記述できます。
Perl -e 'print $1 if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
そのような一致がない場合(ベース名に拡張子がない場合、または拡張子の前のルートが短すぎる場合)は、何も出力されません。要件に応じて、正規表現を調整できます。この正規表現は制約を適用します。
コマンド置換でこれを使用すると、末尾の改行が多すぎるという通常の問題があります。これは、ステファンの回答にも影響する問題です。どちらの場合でも対処できますが、ここでは少し簡単です。
lastpart=$(
Perl -e 'print "$1x" if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
)
lastpart=${lastpart%x} # allow for possible trailing newline
Python2.7
$ echo /path/to/somefile.txt | python -c "import sys, os; print '.'.join(os.path.basename(sys.stdin.read()).split('.')[:-1])[-3:]"
ile
$ echo file.one.two.three | python -c "import sys, os; print '.'.join(os.path.basename(sys.stdin.read()).split('.')[:-1])[-3:]"
two
このbash関数、pathStr()は、あなたが探しているものを実行すると思います。
Awk、sed、grep、Perl、exprは必要ありません。組み込みのbashのみを使用しているため、非常に高速です。
依存するargsNumberおよびisOption関数も含めましたが、それらの機能は簡単にpathStrに組み込むことができます。
依存関数ifHelpShowは、ターミナルコマンドラインまたは [〜#〜] yad [〜#〜] を介してGUIダイアログボックスにヘルプテキストを出力するための多数の従属関係があるため、含まれていません。渡されたヘルプテキストは、ドキュメントに含まれています。 ifHelpShowとその依存パッケージが必要かどうかをアドバイスします。
function pathStr () {
ifHelpShow "$1" 'pathStr --OPTION FILENAME
Given FILENAME, pathStr echos the segment chosen by --OPTION of the
"absolute-logical" pathname. Only one segment can be retrieved at a time and
only the FILENAME string is parsed. The filesystem is never accessed, except
to get the current directory in order to build an absolute path from a relative
path. Thus, this function may be used on a FILENAME that does not yet exist.
Path characteristics:
File paths are "absolute" or "relative", and "logical" or "physical".
If current directory is "/root", then for "bashtool" in the "sbin" subdirectory ...
Absolute path: /root/sbin/bashtool
Relative path: sbin/bashtool
If "/root/sbin" is a symlink to "/initrd/mnt/dev_save/share/sbin", then ...
Logical path: /root/sbin/bashtool
Physical path: /initrd/mnt/dev_save/share/sbin/bashtool
(aka: the "canonical" path)
Options:
--path Absolute-logical path including filename with extension(s)
~/sbin/file.name.ext: /root/sbin/file.name.ext
--dir Absolute-logical path of directory containing FILENAME (which can be a directory).
~/sbin/file.name.ext: /root/sbin
--file Filename only, including extension(s).
~/sbin/file.name.ext: file.name.ext
--base Filename only, up to last dot(.).
~/sbin/file.name.ext: file.name
--ext Filename after last dot(.).
~/sbin/file.name.ext: ext
Todo:
Optimize by using a regex to match --options so getting argument only done once.
Revised:
20131231 docsalvage' && return
#
local _option="$1"
local _optarg="$2"
local _cwd="$(pwd)"
local _fullpath=
local _tmp1=
local _tmp2=
#
# validate there are 2 args and first is an --option
[[ $(argsNumber "$@") != 2 ]] && return 1
! isOption "$@" && return 1
#
# determine full path of _optarg given
if [[ ${_optarg:0:1} == "/" ]]
then
_fullpath="$_optarg"
else
_fullpath="$_cwd/$_optarg"
fi
#
case "$_option" in
--path) echo "$_fullpath" ; return 0;;
--dir) echo "${_fullpath%/*}" ; return 0;;
--file) echo "${_fullpath##*/}" ; return 0;;
--base) _tmp1="${_fullpath##*/}"; echo "${_tmp1%.*}" ; return 0;;
--ext) _tmp1="${_fullpath##*/}";
_tmp2="${_tmp1##*.}";
[[ "$_tmp2" != "$_tmp1" ]] && { echo "$_tmp2"; }
return 0;;
esac
return 1
}
function argsNumber () {
ifHelpShow "$1" 'argsNumber "$@"
Echos number of arguments.
Wrapper for "$#" or "${#@}" which are equivalent.
Verified by testing on bash 4.1.0(1):
20140627 docsalvage
Replaces:
argsCount
Revised:
20140627 docsalvage' && return
#
echo "$#"
return 0
}
function isOption () {
# isOption "$@"
# Return true (0) if argument has 1 or more leading hyphens.
# Example:
# isOption "$@" && ...
# Note:
# Cannot use ifHelpShow() here since cannot distinguish 'isOption --help'
# from 'isOption "$@"' where first argument in "$@" is '--help'
# Revised:
# 20140117 docsalvage
#
# support both short and long options
[[ "${1:0:1}" == "-" ]] && return 0
return 1
}
[〜#〜]リソース[〜#〜]