Find and replace filename recursively in a directory with various names - macos

I have the following file structure
./lib/mylib-client.c
./lib/mylib-object.h
./lib/mylib-rpc-wrapper.c
./lib/mylib-session-base.c
./lib/mylib-session-base.lo
./lib/mylib-session-base.o
./lib/mylibobj.c
./lib/mylibobj.lo
./lib/mylibobj.o
./lib/mylibobj.vala
./lib/mylibrpc-transport.c
./net/cluster/.deps/mylib-config.Po
./net/cluster/.deps/mylib-db.Po
./net/common/mylib-config.c
./net/common/mylib-config.h
./net/common/mylib-db.c
./net/common/mylib-db.h
./net/daemon/.deps/mylib-config.Po
./net/daemon/.deps/mylib-daemon.Po
./net/daemon/.deps/mylib-test.Po
./net/daemon/mylib
./net/daemon/mylib-config.o
./net/daemon/mylib-daemon.c
./net/daemon/mylib-daemon.o
That I want to recursively rename to :
./lib/libvertio-client.c
./lib/libvertio-object.h
./lib/libvertio-rpc-wrapper.c
./lib/libvertio-session-base.c
./lib/libvertio-session-base.lo
./lib/libvertio-session-base.o
./lib/libvertioobj.c
./lib/libvertioobj.lo
./lib/libvertioobj.o
./lib/libvertioobj.vala
./lib/libvertiorpc-transport.c
./net/cluster/.deps/libvertio-config.Po
./net/cluster/.deps/libvertio-db.Po
./net/common/libvertio-config.c
./net/common/libvertio-config.h
./net/common/libvertio-db.c
./net/common/libvertio-db.h
./net/daemon/.deps/libvertio-config.Po
./net/daemon/.deps/libvertio-daemon.Po
./net/daemon/.deps/libvertio-test.Po
./net/daemon/libvertio
./net/daemon/libvertio-config.o
./net/daemon/libvertio-daemon.c
./net/daemon/libvertio-daemon.o
I have found this : Find and replace filename recursively in a directory
But i can't figure out what to change
find . -name "mylib*" | awk '{a=$1; gsub(/mylib/,"libvertio"); printf "mv \"%s\" \"%s\"\n", a, $1}' | sh
Doesn't work. What Am I missing here ?

You can try rename script:
find . -name "mylib*" -exec rename 's/mylib/libvertio/' '{}' \;
Without rename:
find . -name "mylib*" -exec bash -c 'mv "$1" "${1/\/mylib//libvertio}"' - '{}' \;

Related

Filter folders that do not contain any audio files with bash

Given a root folder, how do I filter down subfolders that do not contain any audio files (mp3, wav and flac)? Do I need to set a variable like
folders = find /parentfolder/ -type d
and then pass some expression on ${folders} or is there a one-liner for this?
All the subdirectories of . (we write that into a file):
find . -type d | sort > all_dirs.txt
All subdirectories that do contain an mp3 file (goes into another file):
find . -name "*.mp3" | xargs dirname | sort | uniq > music_dirs.txt
And this is the lines that are only contained in the first file but not the second:
diff --new-line-format="" --unchanged-line-format="" all_dirs.txt music_dirs.txt
If you think oneliners are cool and you are working in bash, here it is a bit more condensed:
diff --new-line-format="" --unchanged-line-format="" <(find . -type d | sort) <(find . -name "*.mp3" | xargs dirname | sort | uniq)

list subdirectories recursively

I want to write a shell script so as to recursively list all different subdirectories which contain NEW subdirectory (NEW is a fixed name).
Dir1/Subdir1/Subsub1/NEW/1.jpg
Dir1/Subdir1/Subsub1/NEW/2.jpg
Dir1/Subdir1/Subsub2/NEW/3.jpg
Dir1/Subdir1/Subsub2/NEW/4.jpg
Dir1/Subdir2/Subsub3/NEW/5.jpg
Dir1/Subdir2/Subsub4/NEW/6.jpg
I want to get
Dir1/Subdir1/Subsub1
Dir1/Subdir1/Subsub2
Dir1/Subdir2/Subsub3
Dir1/Subdir2/Subsub4
How can I do that?
find . -type d -name NEW | sed 's|/NEW$||'
--- EDIT ---
for your comment, sed does not have a -print0. There are various ways of doing this (most of which are wrong). One possible solution would be:
find . -type d -name NEW -print0 | \
while IFS= read -rd '' subdirNew; do \
subdir=$(sed 's|/NEW$||' <<< "$subdirNew"); \
echo "$subdir"; \
done
which should be tolerant of spaces and newlines in the filename
ls -R will list things recursively.
of find . | grep "/NEW/" should give you the type of list you are looking for.
You could try this:
find . -type d -name "NEW" -exec dirname {} \;

Unix find and sort with wildcards

let's suppose i've a folder with some xml file:
a-as-jdbc.xml
z-as-jdbc.xml
fa-jdbc.xml
config.xml
router.xml
paster.xml
cleardown.xml
I would like to pipe the find with some kind of sort command using wildcards and my custom sorting logic.
That because I want that the order of the filename returned will be always the same.
For example, i want always:
1 element: "config.xml"
2 element: "*.as-jdbc.xml"
3 element: "-jdbc.xml" (excluding pattern ".as-jdbc")
4 element: "router.xml"
and so on...
How can i achieve this? Any idea?
I did it in the past using arrays but don't remember exactly how i did now...
Thanks
Not too pretty but :
rules.txt:
config\.xml
.*\.as\-jdbc\.xml
^[^-]*\-jdbc\.xml
router\.xml
Commands:
$ find /path/to/dir > /tmp/result.txt
$ cat rules.txt | xargs -I{} grep -E "{}" /tmp/result.txt
config.xml
a-as-jdbc.xml
z-as-jdbc.xml
fa-jdbc.xml
router.xml
You will have to add the two others patterns needed for paster and cleardown
It is certainly easier to do this in a higher level language like Python.
This is not a sorting problem; it is an ordering problem. As such, you cannot use the Unix sort command.
Inevitably, you will need to make 4 passes anyway so I would do either:
$ find /tmp/alex -name config.xml ; \
> find /tmp/alex -name *-as-jdbc.xml ; \
> find /tmp/alex \( \! -name *-as-jdbc.xml -a -name *-jdbc.xml \) ; \
> find /tmp/alex \( -type f -a \! -name config.xml -a \! -name *-jdbc.xml \)
/tmp/alex/config.xml
/tmp/alex/a-as-jdbc.xml
/tmp/alex/z-as-jdbc.xml
/tmp/alex/fa-jdbc.xml
/tmp/alex/cleardown.xml
/tmp/alex/paster.xml
/tmp/alex/router.xml
Or use grep:
$ find /tmp/alex -type f > /tmp/aaa
$ grep /config.xml /tmp/aaa ; \
> grep -- -as-jdbc.xml /tmp/aaa ; \
> grep -- -jdbc.xml /tmp/aaa | grep -v -- -as-jdbc.xml ; \
> egrep -v '(?:config.xml|-jdbc.xml)' /tmp/aaa
/tmp/alex/config.xml
/tmp/alex/a-as-jdbc.xml
/tmp/alex/z-as-jdbc.xml
/tmp/alex/fa-jdbc.xml
/tmp/alex/cleardown.xml
/tmp/alex/paster.xml
/tmp/alex/router.xml
I recommend just putting your find commands in the order you want:
$ find . -name config.xml; \
> find . -name \*.as-jdbc.xm; \
> find . -name \*-jdbc.xml -a ! -name \*as-jdbc.xml; \
> find . -name router.xml; \
> ... and so on.

Bash: Sort within a directory, concatenate contents of file with smallest entry in new file

I have a set of directories RUN1, RUN2, etc.
In each directory, there is a set of files. In each file, there are two numbers. For example (these are saved as .csv, even though there are clearly no commas here):
RUN1
mod_1.csv
2.32e-00
1.2e-01
mod_b.csv
4.53e-00
1.1e-01
RUN2
mod_a.csv
1.23e-01
0.5e-02
mod3.csv
1.67e-00
0.4e-01
etc.
I would like to do this:
For each directory:
For each file in a directory:
Sort files by the first entry
Save contents and path of file with smallest value of first entry.
For example, above, this would result in a new file containing:
2.32e-00 1.2e-01 ./RUN1/mod_1.csv
1.23e-01 0.5e-02 ./RUN2/mod_a.csv
I started out by trying this:
#!/bin/bash
resultfile="best_results.txt"
for d in $(find . -type d -name 'RUN*' | sort);
do
find "$d" -type f -name 'mod*' -exec awk '{print $0, FILENAME}' {} \; >> "$resultfile"
done
But it gives me both values from all files, like this:
2.32e-00 ./RUN1/mod_1.csv
1.2e-01 ./RUN1/mod_1.csv
4.53e-00 ./RUN1/mod_b.csv
1.1e-01 ./RUN1/mod_b.csv
1.23e-01 ./RUN2/mod_a.csv
0.5e-02 ./RUN2/mod_a.csv
1.67e-00 ./RUN2/mod_3.csv
0.4e-01 ./RUN2/mod_3.csv
Then I figured I needed to use head, but this modification:
find "$d" -type f -name 'mod*' -exec awk '{print $0, FILENAME}' {} \; | head -1 >> "$resultfile"
gave me:
find: `awk' terminated by signal 13
I think I need another sort, and probably head, but I can't quite put this together.
EDIT (for clarity):
I want to look through all of the files in a directory, find the file with the smallest first number, and write the values of that file and the file path to a new file. Then, proceed to the next directory and do the same. In my example:
Directory RUN1 contains files mod_1.csv and mod_b.csv. File mod_1.csv has the smallest first value. I want to write its contents and file path on one line:
2.32e-00 1.2e-01 ./RUN1/mod_1.csv
to a file.
Directory RUN2 contains files, mod_a.csv and mod3.csv. File mod_a.csv has the smallest first value. I want to write its contents and file path on one line:
1.23e-01 0.5e-02 ./RUN2/mod_a.csv
So that the new file looks like this:
2.32e-00 1.2e-01 ./RUN1/mod_1.csv
1.23e-01 0.5e-02 ./RUN2/mod_a.csv
I understand that this was not clear in my pre-edit question. Please ask any questions you have! I'm not sure how to make this more clear.
You'll probably want to remove the newlines during find from what it looks like:
resultfile="best_results.txt"
for d in $(find . -type d -name 'RUN*');
do
find "$d" -type f -name 'mod*' -exec awk '{printf "%s ",$0} END {print "", FILENAME}' {} \;
done | sort >> "$resultfile"
The sorting would normally done at the very end (once all the results are returned from stdout), however, it's unclear how exactly you expect it to be sorted. You could probably get rid of the for loop if you really wanted, since using something such as the following should work similarly:
find RUN* -type f -name 'mod*' -exec awk '{printf "%s ",$0} END {print "", FILENAME}' {} \; | sort -k 2,2 >> "$resultfile"
Use the -k option with sort to specify which column(s) to sort by.
Result (using sort -k 2,2):
1.67e-00 0.4e-01 RUN2/mod3.csv
1.23e-01 0.5e-02 RUN2/mod_a.csv
4.53e-00 1.1e-01 RUN1/mod_b.csv
2.32e-00 1.2e-01 RUN1/mod_1.csv

using find sort and wc -l in the one command

This is how find files using find and show the number of lines in each file
$ find ./ -type f -name "data*.csv" -exec wc -l {} +
380723 ./data_2016-07-07-10-41-13.csv
369869 ./data_2016-07-11-10-42-01.csv
363941 ./data_2016-07-08-10-41-50.csv
378981 ./data_2016-07-12-10-41-28.csv
1493514 total
how do I sort the results by file name? Below is my attempt, but it is not working.
$ find ./ -type f -name "data*.csv" -exec wc -l {} + | sort
1493514 total
363941 ./data_2016-07-08-10-41-50.csv
369869 ./data_2016-07-11-10-42-01.csv
378981 ./data_2016-07-12-10-41-28.csv
380723 ./data_2016-07-07-10-41-13.csv
$ find ./ -type f -name "data*.csv" | sort -exec wc -l {} +
sort: invalid option -- 'e'
Try `sort --help' for more information.
$ find ./ -type f -name "data*.csv" -exec sort | wc -l {} +
find: wc: {}missing argument to `-exec'
: No such file or directory
wc: +: No such file or directory
0 total
$
Can someone offer a solution and correct me so I understand it better?
EDIT1
from man sort
-k, --key=POS1[,POS2]
start a key at POS1 (origin 1), end it at POS2 (default end of line). See POS syntax below
POS is F[.C][OPTS], where F is the field number and C the character position in the field; both are origin 1. If neither -t nor -b is in effect, characters in a field are counted from the beginā€
ning of the preceding whitespace. OPTS is one or more single-letter ordering options, which override global ordering options for that key. If no key is given, use the entire line as the key.
Ismail's suggestion of using sort -k is correct. However, I'm often too lazy to learn (or relearn) how -k works, so here's a cheap solution:
find . -name 'data*.csv' -print0 | sort -z | xargs -0 wc -l
Edit: after some experimentation, I did figure out how -k works:
find . -name 'data*.csv' -exec wc -l {} + | sort -k 2

Resources