shell script : how to replace file name - shell

I want to change some file names with full path similar to this:
/home/guest/test
⟶ /home/guest/.test.log
I tried the command below but it cannot search "/"
string="/home/guest/test"
substring="/"
replacement="/."
echo ${string/%substring/replacement}.log

You can do something like:
for file in /home/guest/*; do
name=${file##*/}
path=${file%/*}
mv "$file" "$path"'/.'"$name"'.log'
done

Created using bash on a mac, so it might work with whatever shell you are using...
string="/home/guest/test"
echo $string | sed 's/\/\([^\/]\{0,\}\)$/\/.\1.log/'
Using simple shell string replacement wasn't going to work since I know of no way you can target the last occurrence of the / sign as the only replacement.
Update:
Actually I came to think of a alternative way if you know that it is always "/two/directories/in"
string="/home/guest/test"
firstpartofstring=$(echo $string | cut -d\/ -f1-3)
lastpartofstring=$(echo $string | cut -d\/ -f4)
echo ${firstpartofstring}/.${lastpartofstring}.log

Related

Regex to extract just a portion of the base filename

I'm pretty new to regex and I'm trying to get the portion of a base filename to reuse later in a variable. I'm running tests with echo in order to see the output but I haven't been able to get it to work how I expect. This is the piece of code I've come up with.
#!/bin/bash
search_dir=/var/FTP/DB
filter="[^_0-9.ra]"
for entry in "$search_dir"/*
do
echo $(basename $entry) #| grep -oP $filter
done
It successfully takes the part of the base filename I want but it's splitting each letter in a new line. Can someone help me get this straighten out?
EDIT:
File in /var/FTP/DB:
TEXT_04-20-20-15-55.zip
LEARN_03-22-19-00-01.rar
Desired output:
TEXT
LEARN
#!/bin/bash
search_dir=.
filter="_.*$";
for entry in "$search_dir"/*
do
echo $(basename $entry) | sed -e "s/$filter//"
done

How to extract a string at end of line after a specific word

I have different location, but they all have a pattern:
some_text/some_text/some_text/log/some_text.text
All locations don't start with the same thing, and they don't have the same number of subdirectories, but I am interested in what comes after log/ only. I would like to extract the .text
edited question:
I have a lot of location:
/s/h/r/t/log/b.p
/t/j/u/f/e/log/k.h
/f/j/a/w/g/h/log/m.l
Just to show you that I don't know what they are, the user enters these location, so I have no idea what the user enters. The only I know is that it always contains log/ followed by the name of the file.
I would like to extract the type of the file, whatever string comes after the dot
THe only i know is that it always contains log/ followed by the name
of the file.
I would like to extract the type of the file, whatever string comes
after the dot
based on this requirement, this line works:
grep -o '[^.]*$' file
for your example, it outputs:
text
You can use bash built-in string operations. The example below will extract everything after the last dot from the input string.
$ var="some_text/some_text/some_text/log/some_text.text"
$ echo "${var##*.}"
text
Alternatively, use sed:
$ sed 's/.*\.//' <<< "$var"
text
Not the cleanest way, but this will work
sed -e "s/.*log\///" | sed -e "s/\..*//"
This is the sed patterns for it anyway, not sure if you have that string in a variable, or if you're reading from a file etc.
You could also grab that text and store in a sed register for later substitution etc. All depends on exactly what you are trying to do.
Using awk
awk -F'.' '{print $NF}' file
Using sed
sed 's/.*\.//' file
Running from the root of this structure:
/s/h/r/t/log/b.p
/t/j/u/f/e/log/k.h
/f/j/a/w/g/h/log/m.l
This seems to work, you can skip the echo command if you really just want the file types with no record of where they came from.
$ for DIR in *; do
> echo -n "$DIR "
> find $DIR -path "*/log/*" -exec basename {} \; | sed 's/.*\.//'
> done
f l
s p
t h

Trimming pathnames beyond a keyword (awk, sed, ?)

I want to trim a pathname beyond a certain point after finding a keyword. I'm drawing a blank this morning.
/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java
I want to find the keyword Java, save the pathname beyond that (tsupdater), then cut everything off after the Java portion.
I don't know if this is what you want, but you can split the pathname into two with:
echo "/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java" | sed 'h;s/.*Java//p;g;s/Java.*/Java/'
Which outputs:
/tsupdater/src/tsupdater.java
/home/quikq/1.0/dev/Java
If you would like to save the second part into a file part2.txt and print the first part, you could do:
echo "/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java" | sed 'h;s/.*Java//;wpart2.txt;g;s/Java.*/Java/'
If you're writing a shell script:
myvar="/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java"
part1="${myvar%Java*}Java"
part2="${myvar#*Java/}"
Hope this helps =)
take one you need:
kent$ echo "/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java"|sed -r 's#(.*Java/[^/]*).*#\1#g'
/home/quikq/1.0/dev/Java/tsupdater
kent$ echo "/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java"|sed -r 's#(.*Java).*#\1#g'
/home/quikq/1.0/dev/Java
kent$ echo "/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java"|sed -r 's#.*Java/([^/]*).*#\1#g'
tsupdater
I'm not entirely sure what you want as output (please specify more clearly), but this command:
echo "/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java" | sed 's/.*Java//'
results in:
/tsupdater/src/tsupdater.java
If you want the preceding part then this command:
echo "/home/quikq/1.0/dev/Java/tsupdater/src/tsupdater.java" | sed 's/Java.*//'
results in:
/home/quikq/1.0/dev/
Like I said, I was having a weird morning, but it dawned on me.
echo /home/quikq/1.0/dev/Java/TSUpdater/src/TSUpdater.java | sed s/Java.*//g
Yields
/home/quikq/1.0/dev
Lots of great tips here for chopping it up different ways though. Thanks a bunch!

Remove part of path on Unix

I'm trying to remove part of the path in a string. I have the path:
/path/to/file/drive/file/path/
I want to remove the first part /path/to/file/drive and produce the output:
file/path/
Note: I have several paths in a while loop, with the same /path/to/file/drive in all of them, but I'm just looking for the 'how to' on removing the desired string.
I found some examples, but I can't get them to work:
echo /path/to/file/drive/file/path/ | sed 's:/path/to/file/drive:\2:'
echo /path/to/file/drive/file/path/ | sed 's:/path/to/file/drive:2'
\2 being the second part of the string and I'm clearly doing something wrong...maybe there is an easier way?
If you wanted to remove a certain NUMBER of path components, you should use cut with -d'/'. For example, if path=/home/dude/some/deepish/dir:
To remove the first two components:
# (Add 2 to the number of components to remove to get the value to pass to -f)
echo $path | cut -d'/' -f4-
# output:
# some/deepish/dir
To keep the first two components:
echo $path | cut -d'/' -f-3
# output:
# /home/dude
To remove the last two components (rev reverses the string):
echo $path | rev | cut -d'/' -f4- | rev
# output:
# /home/dude/some
To keep the last three components:
echo $path | rev | cut -d'/' -f-3 | rev
# output:
# some/deepish/dir
Or, if you want to remove everything before a particular component, sed would work:
echo $path | sed 's/.*\(some\)/\1/g'
# output:
# some/deepish/dir
Or after a particular component:
echo $path | sed 's/\(dude\).*/\1/g'
# output:
# /home/dude
It's even easier if you don't want to keep the component you're specifying:
echo $path | sed 's/some.*//g'
# output:
# /home/dude/
And if you want to be consistent you can match the trailing slash too:
echo $path | sed 's/\/some.*//g'
# output:
# /home/dude
Of course, if you're matching several slashes, you should switch the sed delimiter:
echo $path | sed 's!/some.*!!g'
# output:
# /home/dude
Note that these examples all use absolute paths, you'll have to play around to make them work with relative paths.
You can also use POSIX shell variable expansion to do this.
path=/path/to/file/drive/file/path/
echo ${path#/path/to/file/drive/}
The #.. part strips off a leading matching string when the variable is expanded; this is especially useful if your strings are already in shell variables, like if you're using a for loop. You can strip matching strings (e.g., an extension) from the end of a variable also, using %.... See the bash man page for the gory details.
If you don't want to hardcode the part you're removing:
$ s='/path/to/file/drive/file/path/'
$ echo ${s#$(dirname "$(dirname "$s")")/}
file/path/
One way to do this with sed is
echo /path/to/file/drive/file/path/ | sed 's:^/path/to/file/drive/::'
If you want to remove the first N parts of the path, you could of course use N calls to dirname, as in glenn's answer, but it's probably easier to use globbing:
path=/path/to/file/drive/file/path/
echo "${path#*/*/*/*/*/}" # file/path/
Specifically, ${path#*/*/*/*/*/} means "return $path minus the shortest prefix that contains 5 slashes".
Using ${path#/path/to/file/drive/} as suggested by evil otto is certainly the typical/best way to do this, but since there are many sed suggestions it is worth pointing out that sed is overkill if you are working with a fixed string. You can also do:
echo $PATH | cut -b 21-
To discard the first 20 characters. Similarly, you can use ${PATH:20} in bash or $PATH[20,-1] in zsh.
Pure bash, without hard coding the answer
basenames()
{
local d="${2}"
for ((x=0; x<"${1}"; x++)); do
d="${d%/*}"
done
echo "${2#"${d}"/}"
}
Argument 1 - How many levels do you want to keep (2 in the original question)
Argument 2 - The full path
Taken from vsi_common(original version)
Here's a solution using simple bash syntax that accommodates variables (in case you don't want to hard code full paths), removes the need for piping stdin to sed, and includes a for loop, for good measure:
FULLPATH="/path/to/file/drive/file/path/"
SUBPATH="/path/to/file/drive/"
for i in $FULLPATH;
do
echo ${i#$SUBPATH}
done
as mentioned above by #evil otto, the # symbol is used to remove a prefix in this scenario.

Get just the filename from a path in a Bash script [duplicate]

This question already has answers here:
Extract filename and extension in Bash
(38 answers)
Closed 6 years ago.
How would I get just the filename without the extension and no path?
The following gives me no extension, but I still have the path attached:
source_file_filename_no_ext=${source_file%.*}
Many UNIX-like operating systems have a basename executable for a very similar purpose (and dirname for the path):
pax> full_name=/tmp/file.txt
pax> base_name=$(basename ${full_name})
pax> echo ${base_name}
file.txt
That unfortunately just gives you the file name, including the extension, so you'd need to find a way to strip that off as well.
So, given you have to do that anyway, you may as well find a method that can strip off the path and the extension.
One way to do that (and this is a bash-only solution, needing no other executables):
pax> full_name=/tmp/xx/file.tar.gz
pax> xpath=${full_name%/*}
pax> xbase=${full_name##*/}
pax> xfext=${xbase##*.}
pax> xpref=${xbase%.*}
pax> echo "path='${xpath}', pref='${xpref}', ext='${xfext}'"
path='/tmp/xx', pref='file.tar', ext='gz'
That little snippet sets xpath (the file path), xpref (the file prefix, what you were specifically asking for) and xfext (the file extension).
basename and dirname solutions are more convenient. Those are alternative commands:
FILE_PATH="/opt/datastores/sda2/test.old.img"
echo "$FILE_PATH" | sed "s/.*\///"
This returns test.old.img like basename.
This is salt filename without extension:
echo "$FILE_PATH" | sed -r "s/.+\/(.+)\..+/\1/"
It returns test.old.
And following statement gives the full path like dirname command.
echo "$FILE_PATH" | sed -r "s/(.+)\/.+/\1/"
It returns /opt/datastores/sda2
Here is an easy way to get the file name from a path:
echo "$PATH" | rev | cut -d"/" -f1 | rev
To remove the extension you can use, assuming the file name has only ONE dot (the extension dot):
cut -d"." -f1
$ file=${$(basename $file_path)%.*}
Some more alternative options because regexes (regi ?) are awesome!
Here is a Simple regex to do the job:
regex="[^/]*$"
Example (grep):
FP="/hello/world/my/file/path/hello_my_filename.log"
echo $FP | grep -oP "$regex"
#Or using standard input
grep -oP "$regex" <<< $FP
Example (awk):
echo $FP | awk '{match($1, "$regex",a)}END{print a[0]}
#Or using stardard input
awk '{match($1, "$regex",a)}END{print a[0]} <<< $FP
If you need a more complicated regex:
For example your path is wrapped in a string.
StrFP="my string is awesome file: /hello/world/my/file/path/hello_my_filename.log sweet path bro."
#this regex matches a string not containing / and ends with a period
#then at least one word character
#so its useful if you have an extension
regex="[^/]*\.\w{1,}"
#usage
grep -oP "$regex" <<< $StrFP
#alternatively you can get a little more complicated and use lookarounds
#this regex matches a part of a string that starts with / that does not contain a /
##then uses the lazy operator ? to match any character at any amount (as little as possible hence the lazy)
##that is followed by a space
##this allows use to match just a file name in a string with a file path if it has an exntension or not
##also if the path doesnt have file it will match the last directory in the file path
##however this will break if the file path has a space in it.
regex="(?<=/)[^/]*?(?=\s)"
#to fix the above problem you can use sed to remove spaces from the file path only
## as a side note unfortunately sed has limited regex capibility and it must be written out in long hand.
NewStrFP=$(echo $StrFP | sed 's:\(/[a-z]*\)\( \)\([a-z]*/\):\1\3:g')
grep -oP "$regex" <<< $NewStrFP
Total solution with Regexes:
This function can give you the filename with or without extension of a linux filepath even if the filename has multiple "."s in it.
It can also handle spaces in the filepath and if the file path is embedded or wrapped in a string.
#you may notice that the sed replace has gotten really crazy looking
#I just added all of the allowed characters in a linux file path
function Get-FileName(){
local FileString="$1"
local NoExtension="$2"
local FileString=$(echo $FileString | sed 's:\(/[a-zA-Z0-9\<\>\|\\\:\)\(\&\;\,\?\*]*\)\( \)\([a-zA-Z0-9\<\>\|\\\:\)\(\&\;\,\?\*]*/\):\1\3:g')
local regex="(?<=/)[^/]*?(?=\s)"
local FileName=$(echo $FileString | grep -oP "$regex")
if [[ "$NoExtension" != "" ]]; then
sed 's:\.[^\.]*$::g' <<< $FileName
else
echo "$FileName"
fi
}
## call the function with extension
Get-FileName "my string is awesome file: /hel lo/world/my/file test/path/hello_my_filename.log sweet path bro."
##call function without extension
Get-FileName "my string is awesome file: /hel lo/world/my/file test/path/hello_my_filename.log sweet path bro." "1"
If you have to mess with a windows path you can start with this one:
[^\\]*$
$ source_file_filename_no_ext=${source_file%.*}
$ echo ${source_file_filename_no_ext##*/}

Resources