I am trying to get "abc.txt" out of /this/is/could/be/any/path/abc.txt using Unix command.
Note that /this/is/could/be/any/path is dynamic.
Any idea?
In bash:
path=/this/is/could/be/any/path/abc.txt
If your path has spaces in it, wrap it in "
path="/this/is/could/be/any/path/a b c.txt"
Then to extract the path, use the basename function
file=$(basename "$path")
or
file=${path##*/}
basename path gives the file name at the end of path
Edit:
It is probably worth adding that a common pattern is to use back quotes around commands e.g. `basename ...`, so UNIX shells will execute the command and return its textual value.
So to assign the result of basename to a variable, use
x=`basename ...path...`
and $x will be the file name.
You can use basename /this/is/could/be/any/path/abc.txt
You can use dirname command
$ dirname $path
Related
I have files in directory 'new_kb'. I want to iterate on each file and execute a c++ binary on the file, like below:
kb_dir=./new_kb
for entry in "$kb_dir"/*
do
echo "$entry"
$file_name = parse(entry)
./build "$file_name"/"$file_name".txt
done
One example of 'entry' is:
./new_kb/peopleObj.txt
from the variable path 'entry', I need to parse the string below out:
'peopleObj'
How to do this in a shell script?
Using shell built in parameter expansion:
file_name=${entry##*/}
file_name=${file_name%.txt}
Using basename(1):
file_name=$(basename "$entry" .txt)
Note that whitespace is fundamental to how shell commands are parsed, and all variable declarations should have no whitespace around =.
Use basename
$file_name=$(basename $entry .txt)
I want to read a list of file names stored in a file, and the top level directory is a macro, since this is for a script that may be run in several environments.
For example, there is a file file_list.txt holding the following fully qualified file paths:
$TOP_DIR/subdir_a/subdir_b/file_1
$TOP_DIR/subdir_x/subdir_y/subdir_z/file_2
In my script, I want to tar the files, but in order to do that, tar must know the actual path.
How can I get the string containing the file path to expand the macro to get the actual path?
In the code below the string value echoed is exactly as in the file above.
I tried using actual_file_path=`eval $file_path` and while eval does evaluate the macro, it returns a status, not the evaluated path.
for file_path in `cat $input_file_list`
do
echo "$file_path"
done
With the tag ksh I think you do not have the utility envsubst.
When the number of variables in $input_file_list is very limited, you can substitute vars with awk :
awk -v top_dir="${TOP_DIR}" '{ sub(/$TOP_DIR/, top_dir); print}' "${input_file_list}"
I was using eval incorrectly. The solution is to use an assignment on the right side of eval as follows:
for file_path in `cat $input_file_list`
do
eval myfile=$file_name
echo "myfile = $myfile"
done
$myfile now has the actual expansion of the macro.
I have filepaths of the form:
../healthy_data/F35_HC_532d.dat
I want to extract F35_HC_532d from this. I can remove prefix and suffix from this filename in bash as:
for i in ../healthy_data/*; do echo ${i#../healthy_data/}; done # REMOVES PREFIX
for i in ../healthy_data/*; do echo ${i%.dat}; done # REMOVES SUFFIX
How can I combine these so that in a single command I would be able to remove both and extract only the part that I want?
You can use BASH regex for this like this and print captured group #1:
for file in ../healthy_data/*; do
[[ $file =~ .*/([_[:alnum:]]+)\.dat$ ]] && echo "${BASH_REMATCH[1]}"
done
If you can use Awk, it is pretty simple,
for i in ../healthy_data/*
do
stringNeeded=$(awk -F/ '{split($NF,temp,"."); print temp[1]}' <<<"$i")
printf "%s\n" "$stringNeeded"
done
The -F/ splits the input string on / character, and $NF represents the last field in the string in that case, F35_HC_532d.dat, now the split() function is called with the de-limiter . to extract the part before the dot.
The options/functions in the above Awk are POSIX compatible.
Also bash does not support nested parameter expansions, you need to modify in two fold steps something like below:-
tempString="${i#*/*/}"
echo "${tempString%.dat}"
In a single-loop,
for i in ../healthy_data/*; do tempString="${i#*/*/}"; echo "${tempString%.dat}" ; done
The two fold syntax here, "${i#*/*/}" part just stores the F35_HC_532d.dat into the variable tempString and in that variable we are removing the .dat part as "${tempString%.dat}"
If all files end with .dat (as you confirmed) you can use the basename command:
basename -s .dat /path/to/files/*
If there are many(!) of those files, use find to avoid an argument list too long error:
find /path/to/files -maxdepth 1 -name '*.dat' -exec basename -s .dat {} +
For a shell script which needs to deal if any number of .dat files use the second command!
Do you count this as one step?
for i in ../healthy_data/*; do
sed 's#\.[^.]*##'<<< "${i##*/}"
done
You can't strip both a prefix and suffix in a single parameter expansion.
However, this can be accomplished in a single loop using parameter expansion operations only. Just save the prefix stripped expansion to a variable and use expansion again to remove its suffix:
for file in ../healthy_data/*; do
prefix_stripped="${file##*\/healthy_data\/}"
echo "${prefix_stripped%.dat}"
done
If you are on zsh, one way to achieve this without the need for defining another variable is
for i in ../healthy_data/*; do echo "${${i#../healthy_data/}%.dat}"; done
This removes prefix and suffix in one step.
In your specific example the prefix stems from the fact that the files are located in a different directory. You can get rid of the prefix by cding in this case.
(cd ../healthy_data ; for i in *; do echo ${i%.dat}; done)
The (parens) invoke a sub shell process and your current shell stays where it is. If you don't want a sub shell you can cd back easily:
cd ../healthy_data ; for i in *; do echo ${i%.dat}; done; cd -
In bash I am trying to glob a list of files from a directory to give as input to a program. However I would also like to give this program the list of filenames
files="/very/long/path/to/various/files/*.file"
So I could use it like that.
prompt> program -files $files -names $namelist
If the glob gives me :
/very/long/path/to/various/files/AA.file /very/long/path/to/various/files/BB.file /very/long/path/to/various/files/CC.file /very/long/path/to/various/files/DD.file /very/long/path/to/various/files/ZZ.file
I'd like to get the list of AA BB CC DD ZZ to feed my program without the long pathname and file extension.
However I have no clue on how start there ! Any hint much appreciated !
It's better to use an array to hold the filenames. A string variable will not handle filenames which contain spaces.
Also, you don't need to use the basename command. Instead use bash's built-in string manipulation.
Try this:
files=( /very/long/path/to/various/files/*.file )
for file in "${files[#]}"
do
filename="${file##*/}"
filenameWithoutExtension="${filename%.*}"
echo "$filenameWithoutExtension"
done
Solution with basename for your question
for file in $files
do
file_name=$(basename $file)
file_name_witout_ext=${file_name%.file}
done
edit (generic way to extract filename without (single) extension)
for file in $files
do
file_name=$(basename $file)
file_name_witout_ext=${file_name%.*}
done
Another thing that can happen is to have filename like "archive.tar.gz". In this case you will have two (or multiple extension). You can then use a more greddy operator
for file in $files
do
file_name=$(basename $file)
file_name_witout_ext=${file_name%%.*}
done
It's simpler like this:
files=(/very/long/path/to/various/files/*.file)
names=("${files[#]##*/}") names=("${names[#]%.*}")
progname -files "${files[#]}" -names "${names[#]}"
Or if you could only pass them as a single argument:
progname -files "${files[*]}" -names "${names[*]}"
I'd like to synthesize a string from a directory name in bash. I need to extract the last two path names to make a string.
For example, with an input /a/b/c, I want to make "b_c_HELLO".
How can I do that with bash?
Use basename and dirname:
parent=$(dirname "$input")
output=$(basename "$parent")_$(basename "$input")_HELLO
echo $PATH|awk -F"/" '{print $(NF-1)"_"$NF"_HELLO";}'
A pure bash implementation leveraging Parameter Expansion:
input="a/b/c"
tmp="${input%%/*/*}"
tmp="${tmp#$tmp/}"
output="${tmp/\//_}_HELLO"
Also, see http://mywiki.wooledge.org/BashFAQ/100