Append to PDF file with convert in bash - image

I'm basically downloading some images from a website using wget to then append them into a PDF file using the command line program "convert". But this last thing seems not to work.
I'm getting all the .jpg images and storing them into one folder with no problems, but when I try to merge them into the PDF file, it always reminds with the last appended image. I've read of the convert's -append argument, but it still won't work.
This is how my code looks like:
for file in *.jpg
do
convert "${file}" -append "myfile.pdf"
done
But as logical as it seems, myfile.pdf always ends up having only the last jpg appended image.
I know that using convert like:
convert img1.jpg img2.jpg img3.jpg myfile.pdf
Would do the trick. But as I don't know how many images will I have in the download directory, I cannot hardcode the arguments, so I guess a loop for each image in that directory as I'm trying would be the best solution.
Does anybody know how to achieve my goal? Any help will be much appreciated.
Thanks in advance.

bash automatically expands wildcard arguments (unless if they are quoted or escaped) so even if convert does not support wildcard expansion, bash does. So you could just do
convert *.jpg myfile.pdf
note that if there are too many files, this can result with "arglist too long". But that should be OK for several hundred files.

If your file name follows a pattern like img1.jpg img2.jpg ..... . Then you may also use bash range:
convert img{1..5}.jpg
this will work for img1.jpg img2.jpg img3.jpg img4.jpg img5.jpg . You can change your range as per your requirement.
For converting all the jpg files , answer is already present in other answer by #Jean-François Fabre.

Related

bash: execution of the processing workflow for the files defined in the array

I am working with the post-processing of the images located in the same folder "${vizu}"
# stack all images containet chicont keyword in the name
montage "${vizu}"/*chicont*.png -geometry 1200 -tile x"${rows}"e -mattecolor DarkGoldenrod2 -mode Frame "${vizu}"/${prot}_chimcont_rep${i}.png
Now I need to apply on some of the images pre-processing step in a selective manner:
convert "${vizu}"/${some_keyword}*chicont*.png -distort BarrelInverse 3:30 "${vizu}"/${some_keyword}_chimcont_DISTORTED.png
So basically the pipeline should be:
# list of the pattersn occured somewhere in the filenames that should be considered for pre-processing using convert:
declare -A dataset=( 'some_keyword1' 'some_keyword2' 'some_keyword3')
..
if the name of the file match the pattern presented in dataset
or alternatively for every image mentioned in dataset..
do
convert ..
done
:-)
and then stack all images (included post-processed and intacts) together using montage command. How could I make correctly the list of the keywords (part of the file names) that should be considered for the convert post-processing using either IF or FOR statements ?
Since you keywords are prefix and don't seem to overlap, I would do a simple double loop.
for keyword in "${keywords[#]}"; do
for file in "$vizu/$keyword"*chicont*.png; do
convert $file something something
done
done
You can even collect those files in another array if you need to collate them together after processing.

rename files keeping basename adding extra word and changing extension

I have a script to encode video files. When I run this script, I would like to keep the basename of the file, add a 'modified' string to the filename and also changing the extension.
The following for loop does that with the exception of changing the extension:
for file in *.mkv;
do
encode $file "${file%%.*}_modified.${i#*.}";
done
I'd like that my_file.mkv will become my_file_modified.mp4. The previous loop just converts my_file.mkv into my_file_modified.mkv
How to change also the extension from .mkv to .mp4?
Thanks in advance.
I'm not totally sure if I got your question right, and probably not; but if I did, you should just do this:
for file in *.mkv;
do
encode $file "${file%.*}_modified.mp4";
done
The ${i#*.} part in your previous command actually took the original extension from the file name; you can just omit it and set your own extension instead.
Also, as #M.NejatAydin pointed out in the comment, you should use ${file%.*} instead of ${file%%.*}, to keep the entire original filename if it has a dot inside it.
For example:
$ file="test.file.mkv"
$ echo "${file%%.*}_modified.mp4"
test_modified.mp4 # This is probably NOT what you want
$ echo "${file%.*}_modified.mp4"
test.file_modified.mp4 # This is probably what you want

Exiftool: batch-write metadata to JPEGs from text file

I'd like to use ExifTool to batch-write metadata that have been previously saved in a text file.
Say I have a directory containing the following JPEG files:
001.jpg 002.jpg 003.jpg 004.jpg 005.jpg
I then create the file metadata.txt, which contains the file names followed by a colon, and I hand it out to a coworker, who will fill it with the needed metadata — in this case comma-separated IPTC keywords. The file would look like this after being finished:
001.jpg: Keyword, Keyword, Keyword
002.jpg: Keyword, Keyword, Keyword
003.jpg: Keyword, Keyword, Keyword
004.jpg: Keyword, Keyword, Keyword
005.jpg: Keyword, Keyword, Keyword
How would I go about feeding this file to ExifTool and making sure that the right keywords get saved to the right file? I'm also open to changing the structure of the file if that helps, for example by formatting it as CSV, JSON or YAML.
If you can change the format to a CSV file, then exiftool can directly read it with the -csv option.
You would have to reformat it in this way. The first row would have to have the header of "SourceFile" above the filenames and "Keywords" above the keywords. If the filenames don't include the path to the files, then command would have to be run from the same directory as the files. The whole keywords string need to be enclosed in quotes so they aren't read as a separate columns. The result would look like this:
SourceFile,Keywords
001.jpg,"KeywordA, KeywordB, KeywordC"
002.jpg,"KeywordD, KeywordE, KeywordF"
003.jpg,"KeywordG, KeywordH, KeywordI"
004.jpg,"KeywordJ, KeywordK, KeywordL"
005.jpg,"KeywordM, KeywordN, KeywordO"
At that point, your command would be
exiftool -csv=/path/to/file.csv -sep ", " /path/to/files
The -sep option is needed to make sure the keywords are treated as separate keywords rather than a single, long keyword.
This has an advantage over a script looping over the file contents and running exiftool once for each line. Exiftool's biggest performance hit is in its startup and running it in a loop will be very slow, especially on a large amount of files (see Common Mistake #3).
See ExifTool FAQ #26 for more details on reading from a csv file.
I believe the answer by #StarGeek is superior to mine, but I will leave mine for completeness and reference of a more basic, Luddite approach :-)
I think you want this:
#!/bin/bash
while IFS=': ' read file keywords ; do
exiftool -sep ", " -iptc:Keywords="$keywords" "$file"
done < list.txt
Here is the list.txt:
001.jpg: KeywordA, KeywordB, KeywordC
002.jpg: KeywordD, KeywordE, KeywordF
003.jpg: KeywordG, KeywordH, KeywordI
And here is a result:
exiftool -b -keywords 002.jpg
KeywordD
KeywordE
KeywordF
Many thanks to StarGeek for his corrections and explanations.

OSX / MacOs batch rename hexadecimal filenames to decimal filenames

I want to rename filenames with a hexadecimal part in the name to decimal. For example: MOV12B.MOD, MOV12C.MOD etc. To MOV299.mod, MOV300.MOD.
Can this be done in terminal?
It is possible to rename the extension using:
find . -name "*.MOD" -exec rename 's/\.MOD$/.MPG/' '{}' \;
But how can I rename the files to decimal?
Sure, you can do it with rename, also known as Perl rename and prename which is most simply installed on macOS with homebrew using:
brew install rename
Then the command is:
rename --dry-run 's/[0-9A-F]+/hex($&)/e' *MOD
Sample Output
'MOV10.MOD' would be renamed to 'MOV16.MOD'
'MOV12B.MOD' would be renamed to 'MOV299.MOD'
'MOV12C.MOD' would be renamed to 'MOV300.MOD'
'MOVBEEF.MOD' would be renamed to 'MOV48879.MOD'
If you like what it does, remove the --dry-run part and do it for real.
I would recommend you make a backup before trying this anyway, because if your films are actually named "Film 23.MOD" rather than "MOV12B.MOD" you will get:
'Film 23.MOD' would be renamed to '15ilm 23.MOD'
If you want to put the date in too, you can do:
rename --dry-run 's/[0-9A-F]+/hex($&)/e; s|.MOD| 17/01/2018.MOD|' *MOD
Sample Output
'MOV12A.MOD' would be renamed to 'MOV298 17/01/2018.MOD'
Why couldn't you find it in the man-page? Well, there is a line in there that casually says you can pass a line of Perl code to modify the name. That means that the entire Perl language is available to you - so you could write several pages of code that access a database, run something on a remote machine, or fetch a URL in order to rename your file.
The only tricky thing in my code is the e lurking at the end:
s/search/replace/e
The e means that the second half of the search/replace is actually executed so it is not a straight textual replacement, it is a new program that gets the search string from the left-hand side in $& and can do maths or lookups on it.
I have done some other answers that involve similar techniques...
here,
here,
here.
If you want to put the modification time of the file into its name as well, you need to do a little more work. First, stat() the file before changing its name ;-) Remember you receive the original filename in $_. Then do the the hex to decimal thing, then add in the mtime. Remember Perl uses a dot to concatenate strings together.
So, the command is going to look like this:
rename --dry-run 'my $mtime=(stat($_))[9]; s/[0-9A-F]+/hex($&) . " " . $mtime/e;' *MOD
Sample Output
'MOV12A.MOD' would be renamed to 'MOV298 1516229449.MOD'
If all the substitution and evaluation gets too much, you can always do all your calculations and assign the result to Perl's $_ variable through which you receive the into filename and in which you pass the desired name back to rename. So, for an example:
rename --dry-run 'my $prefix="PREFIX "; my $middle=$_; my $suffix=" SUFFIX"; $_=$prefix . $middle . $suffix;' *MOD
'MOV12A.MOD' would be renamed to 'PREFIX MOV12A.MOD SUFFIX'
Only a real programmer would store his movies with hex names - kudos to you!

Add part of filename as PDF metadata using bash script and exiftool

I have about 600 books in PDF format where the filename is in the format:
AuthorForename AuthorSurname - Title (Date).pdf
For example:
Foo Z. Bar - Writing Scripts for Idiots (2017)
Bar Foo - Fun with PDFs (2016)
The metadata is unfortunately missing for pretty much all of them so when I import them into Calibre the Author field is blank.
I'm trying to write a script that will take everything that appears before the '-', removes the trailing space, and then adds it as the author in the PDF metadata using exiftool.
So far I have the following:
for i in "*.pdf";
do exiftool -author=$(echo $i | sed 's/-.*//' | sed 's/[ \t]*$//') "$i";
done
When trying to run it, however, the following is returned:
Error: File not found - Z.
Error: File not found - Bar
Error: File not found - *.pdf
0 image files updated
3 files weren't updated due to errors
What about the -author= phrase is breaking here? Please could someone enlighten me?
You don't need to script this. In fact, doing so will be much slower than letting exiftool do it by itself as you would require exiftool to startup once for every file.
Try this
exiftool -ext pdf '-author<${filename;s/\s+-.*//}' /path/to/target/directory
Breakdown:
-ext pdf process only PDF files
-author the tag to copy to
< The copy from another tag option. In this case, the filename will be treated as a pseudo-tag
${filename;s/\s+-.*//} Copying from the filename, but first performing a regex on it. In this case, looking for 1 or more spaces, a dash, and the rest of the name and removing it.
Add -r if you want to recurse into subdirectories. Add -overwrite_original to avoid making backupfiles with _original added to the filename.
The error with your first command was that the value you wanted to assign had spaces in it and needed to be enclosed by quotes.

Resources