I have assigned following string to a variable.
line="/remotepath/mypath/localpath/common/location.txt"
If I want to access common location (/remotepath/mypath/localpath/common)
how can I split this in last "/" ?
In most unix-style operating systems, there's a program called dirname which does this for you:
$ line="/remotepath/mypath/localpath/common/location.txt"
$ dirname "$line"
/remotepath/mypath/localpath/common
The command is of course available from any shell, since it's not part of the shell per-se, though you might need to assign the variable differently. For example, in csh/tcsh:
% setenv line "/remotepath/mypath/localpath/common/location.txt"
% dirname "$line"
/remotepath/mypath/localpath/common
If you want to strip off the file using shell commands alone, you'll need to specify what shell you're using, since commands vary. For example, in /bin/sh or similar shells (like bash), you could use "Parameter expansion" (look it up in the man page, there's lots of good stuff):
$ line="/remotepath/mypath/localpath/common/location.txt"
$ echo "${line%/*}
/remotepath/mypath/localpath/common
Hey you can use below command if your line variable contains same number of directories always
echo $line | cut -d "/" -f1-5
line="/remotepath/mypath/localpath/common/location.txt"
path="${line%/*}"
file="${line##*/}"
## contents of the variables after extraction
# path is '/remotepath/mypath/localpath/common'
# file is 'location.txt'
It's called parameter expansion/substring extraction in bash.
Related
I'm trying to write a little script to list a directory from a given variable.
However, I can't run ls at all after reading my input into the variable PATH.
#!/system/bin/sh
echo "enter directory for listing"
read "PATH"
ls "$PATH" -R > list.txt
This exits with:
ls: not found
...and writes nothing to list.txt.
The variable name PATH is already reserved for a different purpose: It lists all the possible locations searched to find commands not built into the shell.
ls is such a command. Thus, when you change the value of PATH, you change the way the shell tries to look for the ls executable; unless the new value of PATH includes a directory with a ls executable in it, any further attempts to run ls (or other commands not built into the shell) will fail.
Instead, use a different variable name -- ideally, including at least one lower-case character, to avoid conflict with (all-uppercase) builtins and environment variables.
Thus, one corrected form might be:
#!/system/bin/sh
echo "enter directory for listing"
IFS= read -r path
ls -R -- "$path" > list.txt
Note that the -R is moved before the "$path" in this case -- while GNU systems will allow optional arguments to be after positional arguments, many older UNIX systems will only treat flags (like -R) as valid if they're found before the first non-flag/option argument.
I fixed it by resetting my iTerm 2.
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 seem to be not able to get this right
When i run this code I need a variable for the filename later on. How should I do thi?
#!/bin/bash
foo="../../../data/audio/serval-data/wav-16bit-16khz/ytdl/balanced_train/vidzyGjrJfE_rg.wav"
echo $foo
echo "${foo%.*}" | cut -d "/" -f10;
# fid=vidzyGjrJfE_rg
I want to use new variable fid to have value "vidzyGjrJfE_rg"
You can use a couple of iterations of shell builtins for this (see #melpomene's answer) but FYI that's exactly what basename exists to do:
$ foo="../../../data/audio/serval-data/wav-16bit-16khz/ytdl/balanced_train/vidzyGjrJfE_rg.wav"
$ fid=$(basename "$foo" '.wav')
$ echo "$fid"
vidzyGjrJfE_rg
You can do it like this:
fid="${foo##*/}"
fid="${fid%.*}"
Just as % removes a matching suffix from a variable, # removes a prefix (and ## removes the longest matching prefix). See https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion.
I'm trying to write a little script to list a directory from a given variable.
However, I can't run ls at all after reading my input into the variable PATH.
#!/system/bin/sh
echo "enter directory for listing"
read "PATH"
ls "$PATH" -R > list.txt
This exits with:
ls: not found
...and writes nothing to list.txt.
The variable name PATH is already reserved for a different purpose: It lists all the possible locations searched to find commands not built into the shell.
ls is such a command. Thus, when you change the value of PATH, you change the way the shell tries to look for the ls executable; unless the new value of PATH includes a directory with a ls executable in it, any further attempts to run ls (or other commands not built into the shell) will fail.
Instead, use a different variable name -- ideally, including at least one lower-case character, to avoid conflict with (all-uppercase) builtins and environment variables.
Thus, one corrected form might be:
#!/system/bin/sh
echo "enter directory for listing"
IFS= read -r path
ls -R -- "$path" > list.txt
Note that the -R is moved before the "$path" in this case -- while GNU systems will allow optional arguments to be after positional arguments, many older UNIX systems will only treat flags (like -R) as valid if they're found before the first non-flag/option argument.
I fixed it by resetting my iTerm 2.
I want to make a script that takes a file path for argument, and cds into its folder.
Here is what I made :
#!/bin/bash
#remove the file name, and change every space into \space
shorter=`echo "$1" | sed 's/\/[^\/]*$//' | sed 's/\ /\\\ /g'`
echo $shorter
cd $shorter
I actually have 2 questions (I am a relative newbie to shell scripts) :
How could I make the cd "persistent" ? I want to put this script into /usr/bin, and then call it from wherever in the filesystem. Upon return of the script, I want to stay in the $shorter folder. Basically, if pwd was /usr/bin, I could make it by typing . script /my/path instead of ./script /my/path, but what if I am in an other folder ?
The second question is trickier. My script fails whenever there is a space in the given argument. Although $shorter is exactly what I want (for instance /home/jack/my\ folder/subfolder), cd fails whith the error /usr/bin/script : line 4 : cd: /home/jack/my\: no file or folder of this type. I think I have tried everything, using things like cd '$shorter' or cd "'"$shorter"'" doesn't help. What am I missing ??
Thanks a lot for your answers
in your .bashrc add the following line:
function shorter() { cd "${1%/*}"; }
% means remove the smaller pattern from the end
/* is the patern
Then in your terminal:
$ . ~/.bashrc # to refresh your bash configuration
$ type shorter # to check if your new function is available
shorter is a function
shorter ()
{
cd "${1%/*}"
}
$ shorter ./your/directory/filename # this will move to ./your/directory
The first part:
The change of directory won't be “persistent” beyond the lifetime of your script, because your script runs in a new shell process. You could, however, use a shell alias or a shell function. For example, you could embed the code in a shell function and define it in your .bash_profile or other source location.
mycdfunction () {
cd /blah/foo/"$1"
}
As for the “spaces in names” bit:
The general syntax for referring to a variable in Bourne shells is: "$var" — the "double quotes" tell the shell to expand any variables inside of them, but to group the outcome as a single parameter.
Omitting the double quotes around $var tells the shell to expand the variable, but then split the results into parameters (“words”) on whitespace. This is how the shell splits up parameters, normally.
Using 'single quotes' causes the shell to not expand any contents, but group the parameters togethers.
You can use \ (backslash-blank) to escape a space when you're typing (or in a script), but that's usually harder to read than using 'single quotes' or "double quotes"…
Note that the expansion phase includes: $variables wild?cards* {grouping,names}with-braces $(echo command substitution) and other effects.
| expansion | no expansion
-------------------------------------------------------
grouping | " " | ' '
splitting | (no punc.) | (not easily done)
For the first part, there is no need for the shorter variable at all. You can just do:
#!/bin/bash
cd "${1%/*}"
Explanation
Most shells, including bash, have what is called Parameter Expansion and they are very powerful and efficient as they allow you to manipulate variables nativly within the shell that would normally require a call to an external binary.
Two common examples of where you can use Parameter Expansion over an external call would be:
${var%/*} # replaces dirname
${var##*/} # replaces basename
See this FAQ on Parameter Expansion to learn more. In fact, while you're there might as well go over the whole FAQ
When you put your script inside /usr/bin you can call it anywhere. And to deal with whitespace in the shell just put the target between "" (but this doesn't matter !!).
Well here is a demo:
#!/bin/bash
#you can use dirname but that's not apropriate
#shorter=$(dirname $1)
#Use parameter expansion (too much better)
shorter=${1%/*}
echo $shorter
An alternate way to do it, since you have dirname on your Mac:
#!/bin/sh
cd "$(dirname "$1")"
Since you mentioned in the comments that you wanted to be able to drag files into a window and cd to them, you might want to make your script allow file or directory paths as arguments:
#!/bin/sh
[ -f "$1" ] && set "$(dirname "$1")" # convert a file to a directory
cd "$1"