I have a bunch of folders:
test_001
test_002
and I would like to replace the prefix test with ftp to get:
ftp_001
ftp_002
One problem: I have access on a Linux-Server with minimal installation. For example, rename is not installed and probably even sed is not installed. so, how can I replace the prefix using pure bash?
Since you have a minimal installation I have tried to make a command that does not require tr, sed or find.
INPUT:
$ tree .
.
├── a
├── b
├── c
├── test_001
└── test_002
2 directories, 3 files
CMD:
for d in */; do mv "${d:0:-1}" "ftp"${d:4:-1}; done
OUTPUT:
tree .
.
├── a
├── b
├── c
├── ftp_001
└── ftp_002
2 directories, 3 files
Explanations about substrings in bash : https://www.tldp.org/LDP/abs/html/string-manipulation.html
This little script may help:
for dir in */
do
mv "$dir" "${dir/test/ftp}"
done
execute it under the parent of your test_00x directory.
It could be written in a compact one-liner:
for dir in */; do mv "$dir" "${dir/test/ftp}"; done
Related
Let's suppose there is a folder with several subfolders. In each subfolder there is a file, that has a different name depending on the folder. For example
basefolder
|________f1_1_1: video_1_1_1.mp4
|________f1_2_1: video_1_2_1.mp4
|
|_ .....
I want to write a shell script that do some processing on these files
So I have
search_dir=/path/to/the/basefolder/
for entry in "$search_dir"*/
do
echo "$entry"
#ls "$entry" #<--------HERE
echo "========================"
done
As you can see I can list the subfolders.
I want to do something like
process video_1_1_1.mp4 video_1_1_1_out.mp4
but the file name varies.
Yes I see that I can perhaps use the entry variable to compose the name of the file, but what if the files don't follow this pattern and the only thing I know is that they start with "video"?
Is there a way to get the name of the file in the folder so as to use it later?
Consider this file tree:
$ tree /tmp/test
/tmp/test
├── one
│ ├── one-1.mp4
│ ├── one-2.mp4
│ ├── one-3.mp4
│ ├── video-1.mp4
│ └── video-2.mp4
└── two
├── two-1.mp4
├── two-2.mp4
├── two-3.mp4
├── video-1.mp4
└── video-2.mp4
2 directories, 10 files
You can use a recursive glob to find all the .mp4 files in that tree:
$ for fn in "/tmp/test/"**/*".mp4"; do echo "$fn"; done
/tmp/test/one/one-1.mp4
/tmp/test/one/one-2.mp4
/tmp/test/one/one-3.mp4
/tmp/test/one/video-1.mp4
/tmp/test/one/video-2.mp4
/tmp/test/two/two-1.mp4
/tmp/test/two/two-2.mp4
/tmp/test/two/two-3.mp4
/tmp/test/two/video-1.mp4
/tmp/test/two/video-2.mp4
Or just the ones starting with video:
$ for fn in "/tmp/test/"**/"video-"*".mp4"; do echo "$fn"; done
/tmp/test/one/video-1.mp4
/tmp/test/one/video-2.mp4
/tmp/test/two/video-1.mp4
/tmp/test/two/video-2.mp4
Instead of echo you can process...
If process involves more than one file, you can use xargs.
You can also use find:
$ find "/tmp/test/" -iname "video*.mp4" -type f
/tmp/test//one/video-1.mp4
/tmp/test//one/video-2.mp4
/tmp/test//two/video-1.mp4
/tmp/test//two/video-2.mp4
Then you would construct a pipe to xargs or use find -exec:
$ find [ what ] -print0 | xargs -0 process # xargs way
$ find [ what ] -exec process {} + # modern find
I'm trying to utilize a bash script to delete some unwanted files with the same name in different directories, eg: text1.txt exists in multiple directories and I wish to remove it in every directory it exists in.
I need the script to delete the unwanted files and then also delete the directory in which that filename 'text1.txt' exists, so if it exists in a folder named 'TextFiles' I need that folder directory to be deleted.
This is my current code I'm working on:
for files in "/*"
do
rm file.1txt file2.txt file3.txt
I'm a bit curious about whether the "/*" will look into all directories and whether the 'do' is working to remove the files stated.
Also, after utilising the 'rm' to remove specific files how do I delete the directory it exists in.
Many thanks!
Before I start, I have to note that the rm command can do some nasty things in your system. Automating it can lead to unintended data loss (system or personal files and folders) if used carelessly.
Now that I said that, imagine the following file structure:
bhuiknei#debian:~/try$ tree
.
├── dir1
│ └── this.txt
└── dir2
├── dir3
│ ├── this
│ └── this.txt
├── notthis.txt
└── this.txt
3 directories, 5 files
To find and filter specific files find and grep are your friends. The "-w" option will match to whole words only (so the notthis.txt is not picked up):
bhuiknei#debian:~/try$ find . | grep -w this.txt
./dir1/this.txt
./dir2/dir3/this.txt
./dir2/this.txt
Now that we have all paths for the files lined up, these can be piped into a while loop where we can delete the files one-by-one. Then the empty directories can be deleted in a second step.
I would not suggest deleting the containing folders forcibly as they might contain other files and folders too.
The following script does the trick:
#!/bin/bash
#Exiting if no file name was given
[[ $# -ne 1 ]] && { echo "Specify a filename to delete in all sub folders"; exit 1; }
#Deleting files matching input parameter
echo "Deleting all files named ${1} in current and sub-directories."
find . | grep -w "$1" | \
while IFS= read LINE; do
rm -v "$LINE"
done
#Deleting only-empty folders
rmdir -v *
exit 0
And the result:
bhuiknei#debian:~/try$ tree
.
├── dir1
│ └── this.txt
├── dir2
│ ├── dir3
│ │ ├── this
│ │ └── this.txt
│ ├── notthis.txt
│ └── this.txt
└── script
3 directories, 6 files
bhuiknei#debian:~/try$ ./script this.txt
Deleting all files named this.txt in current and sub-directories.
removed './dir1/this.txt'
removed './dir2/dir3/this.txt'
removed './dir2/this.txt'
rmdir: removing directory, 'dir1'
rmdir: removing directory, 'dir2'
rmdir: failed to remove 'dir2': Directory not empty
rmdir: removing directory, 'script'
rmdir: failed to remove 'script': Not a directory
bhuiknei#debian:~/try$ tree
.
├── dir2
│ ├── dir3
│ │ └── this
│ └── notthis.txt
└── script
2 directories, 3 files
Also a side note: I didn't test what happens if the working directory is different where the script is located, so make sure to run it locally from the parent dir, or add some protection. Working with absolute paths can be a solution.
Good luck!
You know the extension of the file name and so you can utilise this in a loop parsing the output of find with parameter expansion and so:
find /path -name "file1.txt" | while read var
do
echo "rm -Rf ${var%/file1.txt}" # echo the command
# rm -Rf "${var%/file1.txt}" # execute the command when sure that command list as expected
done
${var%/file1.txt} -
will expand the output from find and expand the output only up to /file1.txt (the directory) rm -Rf will then force removal the directory along with the file
Alternatively you can use printf natively in find to print only the directory without the file:
find /path -name "file1.txt" -printf "%h\n" | while read var
do
echo "rm -Rf $var" # echo the command
# rm -Rf "$var" # execute the command when sure that command list as expected
done
Maybe one of you guys has something like this at hand already? I tried to use robocopy on windows but to no avail. I also tried to write a bash script in linux with find etc... but gave up on that one also ^^ Google search brought no solution also unfortunately. I need this for my private photo library.
Solution could be linux or windows based, both are fine. Any ideas?
I would like to get rid of hundreds of 'intermediary folders'.
I define an 'intermediary folder' as a folder that contains nothing else than exactly one sub-folder. Example
folder 1
file in folder 1
folder 2 <-- 'intermediary folder: contains exactly one sub-folder, nothing else'
folder 3
file in folder 3
What I would like to end up with is:
folder 1
file in folder 1
folder 3
file in folder 3
I do not need the script to be recursive (removing several layers of intermediary folders at once), I'll just run it several times.
Even cooler would be if the script could rename folder 3 in the above example to 'folder 2 - folder 3', but I can live without this feature I guess.
I guess one of you linux experts has a one liner handy for that? ^^
Thank you very much!
Take a look at this code:
#!/usr/bin/env bash
shopt -s nullglob
while IFS= read -rd '' dir; do
f=("$dir"/*)
if ((${#f[#]}==1)) && [[ -d $f ]]; then
mv -t "${dir%/*}" "$f" || continue
rm -r "$dir"
fi
done < <(find folder1 -depth -mindepth 1 -type d -print0)
Explanation:
shopt -s nullglob: allows filename patterns which match no files to expand to a null string
find ... -depth: makes find traverse the file system in a depth-first order
find ... -mindepth 1: processes all directories except the starting-point
find ... -type d: finds only directories
find ... -print0: prints the directories separated by a null character \0 (to correctly handle possible newlines in filenames)
while IFS= read ...: loops over all the directories (the output of find)
f=("$dir"/*): creates an array with all files in the currently processed directory
((${#f[#]}==1)) && [[ -d $f ]]: true if there is only one file and it is a directory
mv -t "${dir%/*}" "$f": moves the only subdirectory one directory above
mv ... || continue: mv can fail if the subdirectory already exists in the directory above. || continue ignores such subdirectory
rm -r "$dir": removes the processed directory
Test run:
$ tree folder1
folder1
├── file1
├── folder2
│ └── folder3
│ └── file3
├── folder4
│ ├── file4a
│ ├── file4b
│ └── file4c
└── folder5
└── folder6
├── file6
└── folder7
└── folder8
└── folder9
├── dir9
└── file9
$ ./script
$ tree folder1
folder1
├── file1
├── folder3
│ └── file3
├── folder4
│ ├── file4a
│ ├── file4b
│ └── file4c
└── folder6
├── file6
└── folder9
├── dir9
└── file9
I am trying to perform search and replace in file names as following:
$ tree
.
├── a_a
│ ├── a_b_c
│ ├── a b c.mkv
│ ├── b_b
│ └── b_c_d
├── b_a
│ ├── a_f_r
│ ├── c_d
│ ├── f_r_e
│ └── r r
├── c_r
│ ├── d_f
│ └── r_a_s.mkv
└── d.mkv
I want to replace the underscores in the file and folder names by spaces. And the way I want to do this is by replacing the underscores in the base names of the files and folder present in the inner directories first and then move up so that the path I am recursing still exists in the next iteration, since if I rename the upper layer directories, in the next iteration the path to access its inner directories and files will become invalid.
I know I can recurse over files using the find command. Now I want to use a tool to perform the replace operation starting with the files inside and then moving outwards. I don't have much experience in writing regex but I think we may be able to do this using grouping in regex, but I am not sure so plese help.
Till now I have been able to figure out that we can use regex groups to access some parts of the file name. To be more specific, we can get the base name of the folders and files using following regex:
rename -n 's!([^/]*\Z)!uc($1)!e' ./*
Using above regex in the rename command we can convert the base name group to uppercase and I want to know how can I replace the underscores in that group to spaces.
PS: Also I know some of you might say this is a duplicate question, but please read it again, I have researched a lot before asking the question and could not find this specific question anywhere.
#!/bin/bash
find -depth | while IFS= read -r fn; do
pnew=$(dirname "$fn")
fnew=$(basename "$fn")
if [[ "$fnew" =~ "_" ]]; then
new="$pnew/${fnew//_/ }"
echo "$fn -> $new"
mv "$fn" "$new"
fi
done
Remarks:
The -depth argument lets find traverse the directories depth-first.
The dirname/basename-split prevents directories from getting renamed along with their children. Only the leaf file/directory may be renamed at a time.
Everything is quoted where needed to allow for spaces in filenames (including incoming filenames).
#!/bin/bash
# man find, search for -type
#
# these are other types:
# b - block special, c - character special, d - directory, p - named pipe
# f - regular file, l - symbolic link, s - socket
# Move directories first, then everything else
for TYPE in d f; do
for NAME in $( find . -type $TYPE -print0 ); do
if [[ $NAME =~ [a-z] ]]; then
NEW_NAME=$NAME
NEW_NAME=${NEW_NAME//[\_]/-} # Change '-' to ' ' if you insist on spaces
echo "renaming '$NAME' to '$NEW_NAME'"
mv "$NAME" "$NEW_NAME"
fi
done
done
I have multiple files in a folder. Every file has different names and have different extensions. I need a way to move each of the files into folders called Archive_1, Archive_2, Archive_n, and so on.
It doesn't matter the order of the file but I need one file per folder.
I was looking for something like sorting the files by name and based on that move the first one to Archive_1, and then the second one to Archive_2, etc. Couldn't find it.
Any help?
Assuming all files are in PWD, you can execute:
i=0
for f in ./*; do
new_dir=Movie_$((++i))
mkdir -p "$new_dir"
mv "$f" "$new_dir"
done
Test ( I created a script called sof with the above command ):
$ touch a b c
$ ./sof
$ tree
.
├── Movie_1
│ └── a
├── Movie_2
│ └── b
└── Movie_3
└── c
3 directories, 3 files