I have a bash script that's like
for i in /path/to/file/*.in; do
./RunCode < "$i"
done
I want to be able to capture the output in something like *.out, with * being the same as *.in. How can I retrieve the text that * is expanded into so that I can reuse it?
By the wording in your question (could be clearer), I assume you wish remove the leading path.
You can use parameter expansion to accomplish what you want:
out_dir="/path/out"
for i in /path/to/file/*.in; do
name="${i##*/}"
./RunCode < "$i" > "$out_dir/${name%.in}.out"
done
This will remove the leading path and the .in extension, name all output files with .out extension, and place them in the directory /path/out.
${i##*/} - Removes all leading characters through the last occurrence of / to get the name of the file with the .in extension.
${name%.in}.out - Removes the trailing .in extension from name and replaces with .out.
Change file suffix with bash:
for i in /path/to/file/*.in; do
./RunCode < "$i" > "${i%.in}.out"
done
From man bash:
${parameter%word}
${parameter%%word}
Remove matching suffix pattern. The word is expanded to produce a
pattern just as in pathname expansion. If the pattern matches a
trailing portion of the expanded value of parameter, then the result
of the expansion is the expanded value of parameter with the shortest
matching pattern (the ``%'' case) or the longest matching pattern
(the ``%%'' case) deleted. If parameter is # or *, the pattern removal
operation is applied to each positional parameter in turn, and the
expansion is the resultant list. If parameter is an array variable
subscripted with # or *, the pattern removal operation is applied
to each member of the array in turn, and the expansion is the resultant
list.
Related
I got the question when i looking other's shell script.
I saw that declared
APP_NAME="${0##*[\\/]}"
Since I can't find any answer, what's the meaning of this code?
It's Shell Parameter Expansion to get script name itself, without path
See bash manual:
${parameter##word}
The word is expanded to produce a pattern and matched according to the rules described below (see Pattern Matching).
If the pattern matches the beginning of the expanded value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the ‘#’ case) or the longest matching pattern (the ‘##’ case) deleted.
Pattern Matching:
*
Matches any string, including the null string. When the globstar shell option is enabled, and ‘*’ is used in a filename expansion context
[…]
Matches any one of the enclosed characters.
Explanation
${parameter<...>} expression means that you can expand shell parameters.
I.e. ${1:-"default_arg_value"} will be expanded to "default_arg_value" if script running without arguments.
0 - is a 0th argument, i.e. script name itself
${0##<pattern>} will delete longest matching to <pattern> part of $0
*[\\/] means any string that ends with \ or / symbol.
So, APP_NAME="${0##*[\\/]}" means that $APP_NAME will be initialized by script name itself, without path.
Sample
Let's suppose you have script a/b/c/d/test.sh:
#!/bin/bash
echo "${0##*[\/]}"
echo "${1#*[\/]}"
echo "${2##[\/]}"
$ bash a/b/c/d/test.sh /tmp/e /tmp/ff
> test.sh
> tmp/e
> tmp/ff
I am wrinting a sed invocation using a shell variable. The variable contains a path name with file name:
sed "file name is '$variable'"
...
variable=/path/path/file.txt
The problem is that I don't need the /path/path/ part. I need just file.txt part in output.
Also my path is dynamic so I am guessing that I need to search (somehow) in a string for a first slash from the ending. How do I do that?
You can use basename to do that:
basename /tmp/a.jpg
a.jpg
You can use the shell's variable substitution feature to remove parts matching a glob pattern:
$ variable=/path/path/file.txt
$ echo ${variable##*/} # Remove longest left part matching "*/"
file.txt
From the bash manual:
${parameter#word}
${parameter##word}
The word is expanded to produce a pattern just as in pathname expansion. If
the pattern matches the beginning of the value of parameter, then the result
of the expansion is the expanded value of parameter with the shortest match-
ing pattern (the ‘‘#’’ case) or the longest matching pattern (the ‘‘##’’
case) deleted. If parameter is # or *, the pattern removal operation is
applied to each positional parameter in turn, and the expansion is the
resultant list. If parameter is an array variable subscripted with # or *,
the pattern removal operation is applied to each member of the array in
turn, and the expansion is the resultant list.
${parameter%word}
${parameter%%word}
The word is expanded to produce a pattern just as in pathname expansion. If
the pattern matches a trailing portion of the expanded value of parameter,
then the result of the expansion is the expanded value of parameter with the
shortest matching pattern (the ‘‘%’’ case) or the longest matching pattern
(the ‘‘%%’’ case) deleted. If parameter is # or *, the pattern removal
operation is applied to each positional parameter in turn, and the expansion
is the resultant list. If parameter is an array variable subscripted with #
or *, the pattern removal operation is applied to each member of the array
in turn, and the expansion is the resultant list.
Sorry for the lame bash question, but I can't seem to be able to work it out.
I have the following simple case:
I have variable like artifact-1.2.3.zip
I would like to get a sub-string between the hyphen and the last index of the dot (both exclusive).
My bash skill are not too strong. I have the following:
a="artifact-1.2.3.zip"; b="-"; echo ${a:$(( $(expr index "$a" "$b" + 1) - $(expr length "$b") ))}
Producing:
1.2.3.zip
How do I remove the .zip part as well?
The bash man page section titled "Variable Substitution" describes using ${var#pattern}, ${var##pattern}, ${var%pattern}, and ${var%%pattern}.
Assuming that you have a variable called filename, e.g.,
filename="artifact-1.2.3.zip"
then, the following are pattern-based extractions:
% echo "${filename%-*}"
artifact
% echo "${filename##*-}"
1.2.3.zip
Why did I use ## instead of #?
If the filename could possibly contain dashes within, such as:
filename="multiple-part-name-1.2.3.zip"
then compare the two following substitutions:
% echo "${filename#*-}"
part-name-1.2.3.zip
% echo "${filename##*-}"
1.2.3.zip
Once having extracted the version and extension, to isolate the version, use:
% verext="${filename##*-}"
% ver="${verext%.*}"
% ext="${verext##*.}"
% echo $ver
1.2.3
% echo $ext
zip
$ a="artifact-1.2.3.zip"; a="${a#*-}"; echo "${a%.*}"
‘#pattern’ removes pattern so long as it matches the beginning of $a.
The syntax of pattern is similar to that used in filename matching.
In our case,
* is any sequence of characters.
- means a literal dash.
Thus #*- matches everything up to, and including, the first dash.
Thus ${a#*-} expands to whatever $a would expand to,
except that artifact- is removed from the expansion,
leaving us with 1.2.3.zip.
Similarly, ‘%pattern’ removes pattern so long as it matches the end of the expansion.
In our case,
. a literal dot.
* any sequence of characters.
Thus %.* is everything including the last dot up to the end of the string.
Thus if $a expands to 1.2.3.zip,
then ${a%.*} expands to 1.2.3.
Job done.
The man page content for this is as follows (at least on my machine, YMMV):
${parameter#word}
${parameter##word}
The word is expanded to produce a pattern just as in pathname
expansion. If the pattern matches the beginning of the value of
parameter, then the result of the expansion is the expanded
value of parameter with the shortest matching pattern (the ``#''
case) or the longest matching pattern (the ``##'' case) deleted.
If parameter is # or *, the pattern removal operation is applied
to each positional parameter in turn, and the expansion is the
resultant list. If parameter is an array variable subscripted
with # or *, the pattern removal operation is applied to each
member of the array in turn, and the expansion is the resultant
list.
${parameter%word}
${parameter%%word}
The word is expanded to produce a pattern just as in pathname
expansion. If the pattern matches a trailing portion of the
expanded value of parameter, then the result of the expansion is
the expanded value of parameter with the shortest matching pat-
tern (the ``%'' case) or the longest matching pattern (the
``%%'' case) deleted. If parameter is # or *, the pattern
removal operation is applied to each positional parameter in
turn, and the expansion is the resultant list. If parameter is
an array variable subscripted with # or *, the pattern removal
operation is applied to each member of the array in turn, and
the expansion is the resultant list.
HTH!
EDIT
Kudos to #x4d for the detailed answer.
Still think people should RTFM though.
If they don't understand the manual,
then post another question.
Using Bash RegEx feature:
>str="artifact-1.2.3.zip"
[[ "$str" =~ -(.*)\.[^.]*$ ]] && echo ${BASH_REMATCH[1]}
I think you can do this:
string=${a="artifact-1.2.3.zip"; b="-"; echo ${a:$(( $(expr index "$a" "$b" + 1) - $(expr length "$b") ))}}
substring=${string:0:4}
The last step removes the last 4 characters from the string. There's some more info on here.
I have a bash for loop like this:
for i in /long/path/filename*; do
echo ${i%filename*}/other/path/c01/{magic};
done
Now I would like to get {magic} replaced by the text matched by *
Maybe like this:
for i in /long/path/filename*; do
A=${i/\/long\/path\/filename/}
echo ${i%filename*}/other/path/c01/${A}
done
for i in /long/path/filename*;
do echo ${i%filename*}/other/path/c01/${i#/long/path/filename};
done
From man bash:
${parameter#word}
${parameter##word}
The word is expanded to produce a pattern just as in pathname
expansion. If the pattern matches the beginning of the value of
parameter, then the result of the expansion is the expanded
value of parameter with the shortest matching pattern (the ‘‘#’’
case) or the longest matching pattern (the ‘‘##’’ case) deleted.
If parameter is # or *, the pattern removal operation is applied
to each positional parameter in turn, and the expansion is the
resultant list. If parameter is an array variable subscripted
with # or *, the pattern removal operation is applied to each
member of the array in turn, and the expansion is the resultant
list.
This question already has answers here:
Syntax with pound and percent sign after shell parameter name [duplicate]
(2 answers)
Closed 3 years ago.
Here is an example to get different parts of a filename
bash-3.2$ pathandfile=/tmp/ff.txt
bash-3.2$ filename=$(basename $pathandfile)
bash-3.2$ echo $filename
ff.txt
bash-3.2$ echo ${filename##*.}
txt
bash-3.2$ echo ${filename%.*}
ff
I was wondering what does ## and % mean in the patterns. How is the patten matching working?
Thanks and regards!
The manpage for bash says:
${parameter#word}
${parameter##word}
Remove matching prefix pattern. The word is expanded to produce a pattern just as
in pathname expansion. If the pattern matches the beginning of the value of parameter, then the result of the expansion is the expanded value of parameter with the
shortest matching pattern (the # case) or the longest matching pattern (the
## case) deleted. If parameter is # or *, the pattern removal operation is
applied to each positional parameter in turn, and the expansion is the resultant
list. If parameter is an array variable subscripted with # or *, the pattern
removal operation is applied to each member of the array in turn, and the expansion
is the resultant list.
${parameter%word}
${parameter%%word}
Remove matching suffix pattern. The word is expanded to produce a pattern just as
in pathname expansion. If the pattern matches a trailing portion of the expanded
value of parameter, then the result of the expansion is the expanded value of
parameter with the shortest matching pattern (the % case) or the longest matching pattern (the %% case) deleted. If parameter is # or *, the pattern removal
operation is applied to each positional parameter in turn, and the expansion is the
resultant list. If parameter is an array variable subscripted with # or *, the
pattern removal operation is applied to each member of the array in turn, and the
expansion is the resultant list.
From http://tldp.org/LDP/abs/html/string-manipulation.html:
${string##substring}
Deletes longest match of $substring from front of $string.
and
${string%substring}
Deletes shortest match of $substring from back of $string.
See http://tldp.org/LDP/abs/html/string-manipulation.html.
${string##substring}
Deletes longest match of $substring from front of $string.
${string%substring}
Deletes shortest match of $substring from back of $string.