Go to parent directory inline - bash

I was wondering if it’s possible to go to parent directory of a file inline. I know I could type cd .. and it would. However, what if I wanted do something like echo $(find . -name “xyz.png”)..and it would return the parent directory of the file instead of the path to file. Or instead of a file I search for a folder, and want to return path to the parent directory.

You could use dirname to strip off the last part of a path. Combined with find in your examples it would give you just the parent directory of whatever was found. You could use that in cd as in cd $(find -name "xyz.png" | xargs dirname) if that's the sort of thing you're trying to do.
You can also use the -type d option to find to have it only find directories if you want to match directory names instead of filenames.

UNTESTED:
function dirs_ofFile
{
find -name "$1" | xargs dirname
}
then
$ cd $(dirs_ofFile xyz.png | sed 1q) # in case there is more than one.

Related

Renaming multiple files in a nested structure

I have a directory with this structure:
root
|-dir1
| |-pred_20181231.csv
|
|-dir2
| |-pred_20181234.csv
...
|-dir84
|-pred_2018123256.csv
I want to run a command that will rename all the pred_XXX.csv files to pred.csv.
How can I easily achieve that?
I have looked into the rename facility but I do not understand the perl expression syntax.
EDIT: I tried with this code: rename -n 's/\training_*.csv$/\training_history.csv/' *.csv but it did not work
Try with this command:
find root -type f -name "*.csv" -exec perl-rename 's/_\d+(\.csv)/$1/g' '{}' \;
Options used:
-type f to specify file or directory.
-name "*.csv" to only match files with extension csv
-exec\-execdir to execute a command, in this case, perl-rename
's/_\d+(\.csv)/$1/g' search a string like _20181234.csv and replace it with .csv, $1 means first group found.
NOTE
Depending in your S.O. you could use just rename instead of perl-rename.
Use some shell looping:
for file in **/*.csv
do
echo mv "$(dirname "$file")/$(basename "$file")" "$(dirname "$file")/pred.csv"
done
On modern shells ** is a wildcard that matches multiple directories in a hierarchy, an alternative to find, which is a fine solution too. I'm not sure if this should instead be /**/*.csv or /root/**/*.csv based on tree you provided, so I've put echo before the 'mv' to see what it's about to do. After making sure this is going to do what you expect it to do, remove the echo.

How to replace part of filename recursively in terminal / .zsh?

how can I replace a part of the filename, of a certain type (.zip), with another string, recursively through all potential nested subdirectories?
This is my filesystem structure:
dir/
|
subdir/
|
filename_strToReplace.zip
|
subdir/
|
subdir
|
filename_strToReplace.zip
filename_strToReplace.zip
filename_strToReplace.zip
So as you can see, files whose filenames need to be modiffied can be nested few levels deep. I have some moderate terminal and shell experience but not real scripting.
I believe the solution is the combination of mv, RegEx (which I can use pretty decently) and a for loop.
For what it's worth I am on a Mac, using "default" terminal (haven't messed with this) with Oh-my-zshell.
Thanks!
Using find and rename commands you can achieve that:
find . -name '*strToReplace*' | xargs -I{} rename 's/strToReplace/replacement/' {}
find search all files whose name contains strToReplace.
Then rename uses a regex to rename those files.
Use zmv:
autoload zmv
zmv -n '(dir/**/filename)_(.*).zip' '($1)_replacementStr.zip'
Remove the -n to actually perform the rename after verifying that the command will do what you want.
In bash you could achieve this using find + a custom function
#!/bin/bash
function namereplacer()
{
for file in "$#"
do
mv "$file" "${file/%stringToReplace.zip/newstring.zip}"
done
}
export -f namereplacer
find /base/path/ -depth -type f -name "*stringToReplace.zip" \
-exec bash -c 'namereplacer "$#"' _ {} +
# The 'exec {} +' form builds the command line, see find man
Note Replace /base/path with your path to base folder
I used rename similar to sjsam's answer to create a shell script. My use case was to remove .bak extension from the end of the first filename that matched the .tsx pattern:
dir=$1
extensionToChange=.bak
for file in $(find $dir -type f -name *.tsx$extensionToChange); do
echo $file
mv "$file" "${file/$extensionToChange/}"
break;
done
Had to grant execute permission on the script with chmod +x rename_first.sh
Example execution: ./rename_first.sh ../UI/test/src

How to delete a file in any of the directories or subdirectories except one subdirectory

I want to delete a file from a directory which contains many subdirectories but the deletion should not happen in one subdiretory(searc) whose name is already predefined but path varies as shown below.So now how to delete a file i am using the below command
find . -type f -name "*.txt" -exec rm -f {} \;
this command deletes all the files in the directory.So How can we delete the file without serching that subdirectory.
The subdirectory file name will be same but the path will different
for eg
Main
|
a--> searc
|
b-->x--->searc
|
c-->y-->x-->searc
now the
the subdirectory not to be searched can be present any where as shown above
I think you want the -prune option. In combination with a successful name match, this prevents descent into the named directories. Example:
% mkdir -p test/{a,b,c}
% touch test/{a,b,c}/foo.txt
% find test -name b -prune -o -name '*.txt' -print
test/a/foo.txt
test/c/foo.txt
I am not completely sure what you're asking, so I can give only somewhat generic advice.
You already know the -name option. This refers to the filename only. You can, however, also use -wholename (a.k.a. -path), which refers to the full path (beginning with the one given as first option to find).
So if you want to delete all *.txt files except in the foo/bar subdirectory, you can do this:
find . -type f -name "*.txt" ! -wholename "./foo/bar/*" -delete
Note the -delete option; it doesn't require a subshell, and is easier to type.
If you would like to exclude a certain directory name regardless of where in the tree it might be, just don't "root" it. In the above example, foo/bar was "rooted" to ./, so only a top-level foo/bar would match. If you write ! -wholename "*/foo/bar/*" instead (allowing anything before or after via the *), you would exclude any files below any directory foo/bar from the operation.
You can use xargs instead of the exec
find .... <without the --exec stuff> | grep -v 'your search' | xargs echo rm -f
Try this first. If it is satisfactory, you can remove the echo.

parsing and changing the files in all sub directories

I would like to parse all the files *.c in the sub directories and prefix a string to the file name and place file in the same sub-directory.
For example, if there's a file in dir1/subdir1/test.c , I would like to change that file name to xyztest.c and place it in dir1/subdir1/. How to do that?
I would like to do in bash script.
Thanks,
What you need is:
Find all c files in a directory (use find command)
Separate the filname and dirname (use basename and dirname)
Move dirname/filename to dirname/prefix_filename
That should do it.
A find command with while loop should do that:
PREFIX=xyz;
while read line
do
path="$(dirname $line)"
base="$(basename $line)";
mv "${line}" "$path/${PREFIX}${base}"
done < <(find dir1 -name "*.c")
find dir -name '*.c' -printf 'mv "%p" "%h/xyz%f"\n' | sh
This will fail if you have file names with double quotes, or varous other shell metacharacters; but if you don't, it's a nice one-liner.

Change directories

I am having some trouble in doing some comands on shell.
My problem is that I want to change directories more specifically to a directory which I don't know but that contains the file named xxx.
How can I change directly to that directory that contains that file?
If I knew the names of the directories that contained that file would be easier because I only had to use cd ~/Name of directory.
Can anyone help me?
thanks
If you have GNU find:
cd "$(find /startdir -name 'filename' -printf %h -quit)"
You can replace "/startdir" with any valid directory, for example /, . or `~/.
If you want to cd to a directory which is in the $PATH that contains an executable file:
cd "$(dirname "$(type -P "filename")")" # Bash
or
cd "$(f=$(type -P "ksh"); echo "${f%/*}")" # Bash
or
cd "$(dirname "$(which "filename")")"
If you don't know where a file is, go to the root of the system and find it:
cd /
find . -iname filename
In several linux systems you could do:
$ cd `find . -name "filename" | xargs dirname`
But change "filename" to the file you want to find.
BASH
cd `find . -name "*filename*" | head -1`
This is kind of a variation to Qiau answer. Finds the first file which contains the string filename and then change the current directory to its location.
* is a wild card, there may be something before and/or after filename.

Resources