How to use Makefile with gnuplot? - makefile

(/)-----------------------+------------+
| | |
| | |
(task#1)--------+ (data) (images)
| | | |
(gnuplot) Makefile input.dat output.png
| . ^
plotting.gpi<....input....<.... .
. .
.........>.......output........>.......
According to my diagram, my question is that how to write Makefile to work with gnuplot this case?
The detail is:
I have plotting.gpi (gnuplot script) that read input from input.dat and generate output to output.png file. To execute script, we just type gnuplot path/to/plotting.gpi where path/to/file depends on where do you execute gnuplot command if inside gnuplot folder. just gnuplot plotting.gpi is enough.
What had i tried?
I tried to write a very simple Makefile but seem like my understanding not good enough. my Makefile had some problem about file path and sometimes code in Makefile did not execute some lines of code.

You have to create a file structure like
.
├── data
│   └── input.dat
├── images
│   └── output.png
├── Makefile
└── task1
├── gnuplot
│   └── ploting.gpi
└── Makefile
You can type make either in root directory or in task1 directory.
The files have the following text
Makefile
all :
make all -C task1
clean:
make clean -C task1
task1/Makefile
IMAGES=../images/output.png
all : $(IMAGES)
clean:
rm $(IMAGES)
../images/output.png : gnuplot/ploting.gpi ../data/input.dat
gnuplot gnuplot/ploting.gpi
ploting.gpi
set term pngcairo
set output '../images/output.png'
plot '../data/input.dat' using 1:2 with lp
set term x11
input.dat
1 1
2 2
3 0
4 2
5 3
The output.png is

Related

How to rename files recursively with Bash

I am trying to make a bash script that should replace any occurrence of a given pattern with an other, given, expression in any path in a given directory. For instance, if I have the following tree structure:
.
|- file1
|- file-pattern-pattern.html
|- directory-pattern/
| |- another-pattern
| \- pattern.pattern
\- other-pattern/
\- a-file-pattern
it should end up looking like
.
|- file1
|- file-expression-expression.html
|- directory-expression/
| |- another-expression
| \- expression.expression
\- other-expression/
\- a-file-expression
The main issue I have is that most solution I have found make either usage of the ** glob pattern alongside with a shopt -s globstar nullglob or find to execute rename on all the files, but since I actually change the name of a directory during that operation, it breaks with messages like find: ./directory-pattern: No such file or directory or rename: ./directory-pattern/another-expression: not accessible: No such file or directory.
The second issue is that, according to rename'a man page, it "will rename the specified files by repalcing the first occurrence" of the pattern, not all occurrences, and I didn't find any option to overwrite this behavior. Of course, I don't want to "just run rename with -v until it doesn't spit anything anymore", which just sounds silly.
So the question is: how do I achieve that bulk-renaming in Bash?
Edit: leave only the 1-pass solution that apparently works as well as the 2-passes.
You'll probably have to explore the hierarchy depth first. Example with find and a bash exec script:
$ find . -depth -name '*pattern*' -exec bash -c \
'f=$(basename "$1"); d=$(dirname "$1"); \
mv "$1" "$d/${f//pattern/expression}"' _ {} \;
Demo:
$ tree .
.
├── file-pattern-pattern.html
├── file1
├── foo-pattern
│   └── directory-pattern
│   ├── another-pattern
│   └── pattern.pattern
└── other-pattern
└── a-file-pattern
$ find . -depth -name '*pattern*' -exec bash -c \
'f=$(basename "$1"); d=$(dirname "$1"); \
mv "$1" "$d/${f//pattern/expression}"' _ {} \;
$ tree .
.
├── file-expression-expression.html
├── file1
├── foo-expression
│   └── directory-expression
│   ├── another-expression
│   └── expression.expression
└── other-expression
└── a-file-expression
Explanation: -depth tells find to process each directory's contents before the directory itself. This avoids one of the issues you encountered when referring to a directory that was already renamed. The bash script uses simple pattern substitutions to replace all occurrences of string pattern by string expression.

how can I process a file that has different names depending on the folder (in other words get the name first)?

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

Duplicating a nested set of folders in bash/fish

I have a folder full of other folders. Within each of these folders, there also exists another folder that I want to duplicate, but with a new name (same for all copies).
For example:
├── application
│   └── foo
│ └── bar
│   └── redacted.txt
│
├── something_different
│   └── foo
│ └── bar
│   └── RobotoMono.ttf
So every top level folder has a "foo/bar/" folder. I'd like to clone the "bar" folder (and the contents) so there is a "bar2" folder under each "foo" folder.
Then it would look like this:
├── application
│ └── foo
│ └── bar
│ └── redacted.txt
│ └── bar2
│ └── redacted.txt
│
├── something_different
│ └── foo
│ └── bar
│ └── RobotoMono.ttf
│ └── bar2
│ └── RobotoMono.ttf
I can successfully get the list with "find". Here is what I have tried:
find . -name bar -exec cp -r '{}' '{}'/bar2 \;
find . -name bar | xargs cp -r /bar2
And of course theses don't work and leave some nice looping that was fun to clean up. Thank you for reading and explaining what I'm doing incorrectly, or if I'm even close to what I should be doing.
First of all the single quotes around the parentheses will give you a string not the actual target (the directory) that you want. Removing these and copying to one level above "bar" seems to work (bash):
#> mkdir -p application/foo/bar && touch application/foo/bar/redacted.txt
#> find application -name bar -exec cp -r {} {}/../bar2 \;
#> ls -l application/foo/ | awk '{ print $NF }'
./
../
bar/
bar2/
#> ls -l application/foo/bar2/ | awk '{ print $NF }'
./
../
redacted.txt
find . \
-path '.*/foo/bar' \
-type d \
-exec sh -c 'p="${0%/*}"; n="${0##*/}"; cp -rp -- "$p/$n" "$p/${n}2"' {} \;
-exec sh -c Executes the inline shell script
Here is the content of the inline shell script with added comments:
# Extract the path before /bar from argument 0
p="${0%/*}"
# Extract the trailing name bar or anything else from argument 0
n="${0##*/}"
# Perform a recursive copy with preserved permissions
# from the source to the destination with suffix2
cp -rp -- "$p/$n" "$p/${n}2"

Bash: How to replace a prefix of folders

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

Find, move, and create empty file in place of file that was moved

I'm looking to make a crontab that will search through a directory and all subdirectories and find all files with extension *.mkv then move them to a different directory and create an empty file with the same name and extension in place of the original file.
So it would look like this:
find *.mkv in subdirectories of /home/user/directoryA/~
move *.mkv to /home/user/directoryB/
create empty *.mkv with same filename as the original in place of file in /home/user/directoryA/~
What would be the best way to accomplish this?
The process isn't too difficult if you recognize that when forming your new directory names, your old base directory will simply be a substring within the new directory name. Bash provides a parameter expansion with substring replacement that is tailor made for this process.
Essentially, you find each file below your source directory with the *.mkv extension, you use parameter expansion with substring replacement to form the new full-filename containing your destination directory, (e.g. nffn="${ffn/$srcdir/$destdir}", where ffn is short for full-filename and nffn short for new full-filename)
With your new full-filename formed containing the updated path, it is just a matter of making sure the destination directory exists before moving the file. mkdir -p is perfect here as it will create the full path, and will not complain if the directory already exists. You simply use a parameter expansin with substring removal to isolate the new directory from the new full-filename to pass to mkdir -p, and finally, you check that mkdir -p succeeds or you handle the error, e.g.
## create new directory, handle error if create fails
mkdir -p "${nffn%/*}" || {
echo "error: creating '${nffn%/*}'" >&2
exit 1
}
Putting all the pieces together, you can do what you are attempting with a short script similar to the following.
#!/bin/bash
## source and destination directories, file pattern
# (note: to change destdir, two arguments required
# to change patrn, three arguments required)
srcdir="${1:-/home/david/dev/src-c/tmp/debug/AAA}"
destdir="${2:-/home/david/dev/src-c/tmp/debug/BBB}"
patrn="${3:-*.mkv}"
while read -r ffn; do ## loop over each full-filename
nffn="${ffn/$srcdir/$destdir}" ## form new full-filename
## create new directory, handle error if create fails
mkdir -p "${nffn%/*}" || {
echo "error: creating '${nffn%/*}'" >&2
exit 1
}
mv "$ffn" "$nffn" ## move full-filename to new full-filename
touch "$ffn" ## touch full-filename for zero original
done < <(find "$srcdir" -name "$patrn")
(note: you can pass the directories and file pattern as positional parameters, but note, if you pass more than 1, you must pass each required parameter (or you could implement getotp))
Initial Directories AAA & BBB
$ tree AAA
AAA
├── a.mkv
├── b.mkv
├── dir1
│   ├── a.mkv
│   └── b.mkv
├── dir2
│   ├── a.mkv
│   └── b.mkv
├── dir3
│   ├── a.mkv
│   └── b.mkv
└── dira
├── a.mkv
└── b.mkv
$ tree BBB
BBB [error opening dir]
Final Directories AAA & BBB
$ bash mvemptydir.sh
$ tree AAA
AAA
├── a.mkv
├── b.mkv
├── dir1
│   ├── a.mkv
│   └── b.mkv
├── dir2
│   ├── a.mkv
│   └── b.mkv
├── dir3
│   ├── a.mkv
│   └── b.mkv
└── dira
├── a.mkv
└── b.mkv
$ tree BBB
BBB
├── a.mkv
├── b.mkv
├── dir1
│   ├── a.mkv
│   └── b.mkv
├── dir2
│   ├── a.mkv
│   └── b.mkv
├── dir3
│   ├── a.mkv
│   └── b.mkv
└── dira
├── a.mkv
└── b.mkv
Look things over and let me know if you have further questions.
you can write a script like this :
#!/bin/bash
cd /[ADDRESS]
find . -name *.mkv > /tmp/find_result.txt
mv `cut -f1 /tmp/find_result.txt` /backup/
touch `cut -f1 /tmp/find_result.txt`
1- go to your directory that you want to find this files
2- find all .mkv files and send the result to a file like /tmp/find_result.txt in this example
3- move all files (that save in file "/tmp/find_result.txt") to your desired directory (like "/backup" in this example)
4- finaly create empty file with same name (that save in file "/tmp/find_result.txt")
you can add this script to crontab.
You could use a loop to do this for each file matching your criteria!
for f in `find . -name *.mkv`; do
mv $f /home/user/directoryB/
touch $f
done;
If you wanted to get fancy you could put this into a script and accept directoryA/B as arguments:
for f in `find $1 -name *.mkv`; do mv $f $2; touch $f; done;
and run as ./script.sh /home/user/directoryA/~ /home/user/directoryB/

Resources