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
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 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
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 -
I'm here to know how to split a file name that was found by a ls comand :D. Let me explain better...
I have a variable
images=$( ls | grep .img )
And then i want to split the result of the search, because i just want the name before the .img, so a nice idea is use IFS.
IFS=. read -r disk <<< $image
Pretty nice, but when a do an echo with the $disk variable, what i see is a ".img" just that, i want to recover where is before that dot.
Thank you all, and sorry for any mistake :)
Use the stream editor sed! Example:
echo "myImage.jpg" | sed 's/.jpg//'
That s means "substitute", and you substitute the part between the first two slashes for the part between the second and third slash, so in the example, ".jpg" for the empty string.
That's all!
Since you mention using <<<, I'll assume you are using a shell that supports arrays (specifically, bash. Other shells--notably zsh--may use different syntax for what I am about to describe).
images=( *.img ) # No need to parse ls, which is generally a bad idea
Assuming there is only one matching file, you can then use
disk=${images%.img}
to strip .img from the file name and save the remaining portion in disk. If there could be multiple matches, you can apply the extension stripping to each element of the array and store the result in a second array.
disks=( "${images[#]%.img}" )
basename is what you want.
From the man page:
basename - strip directory and suffix from filenames
EXAMPLES
basename /usr/bin/sort
Output "sort".
basename include/stdio.h .h
Output "stdio".