I have 4000 .jpeg image files to which I want to add Latitude & Longitude using exiftool. I have a text file having :
First Column = Image filenames serial-wise from 1 to 4000
Second Column = Latitude
Third Column = Longitude
How do I add longitudes and latitudes to images with some script ?
I have not tried the script, so there is obviously some scope for optimization. For the time being you can try this.
Considering file named coordinates have the columns as described in the question, I have written this script accordingly.
#!/bin/bash
# Loop for every line in the coordinates file
cat coordinates | while IFS= read -r line
do
# Get Image name from Ist, Latitude from 2nd & Longitude from 3rd column.
IMAGE=`echo "$line" | awk '{print $1}'`
LAT=`echo "$line" | awk '{print $2}'`
LONG=`echo "$line" | awk '{print $3}'`
# Assign variables values into "exiftool" command
exiftool -exif:gpslatitude="$LAT" -exif:gpslatituderef=S -exif:gpdlongitude="$LONG" -exif:gpslongituderef=E "$IMAGE"
done
Some points to consider :
If exif tags don't work for you, you may use XMP tags. In that case the command-line would be like this :
exiftool -XMP:GPSLatitude="$LAT" -XMP:GPSLongitude="$LONG" -GPSLatitudeRef="South" -GPSLongitudeRef="East" "$IMAGE"
If you don't have References values for GPSLatitudeRef & GPSLongitudeRef positions, just use -P, and It should run fine :
exiftool -XMP:GPSLatitude="$LAT" -XMP:GPSLongitude="$LONG" -P "$IMAGE"
-P option preserves and overwrites the values for tags passed to it, ignoring the rest of tags that are also needed.
If you wish to add more or less tags, please refer to GPS Tags.
Feel free to add in more details.
Related
I've been trying to compare two csv file using simple shell script but I think the code that I was using is not doing it's job. what I want to do is, compare the two files using Column 6 from first.csv and Column 2 in second.csv and when it matches, it will output the line from first.csv. see below as an example
first.csv
1,0,3210820,0,536,7855712
1,0,3523340820,0,36,53712
1,0,321023423i420,0,336,0255712
1,0,321082234324,0,66324,027312
second.csv
14,7855712,Whie,Black
124,7855712,Green,Black
174,1197,Black,Orange
1284,98132197,Yellow,purple
35384,9811123197,purple,purple
13354,0981123131197,green,green
183434,0811912313127,white,green
Output should be from the first file:
1,0,3210820,0,536,7855712
I've been using the code below.
cat first.csv | while read line
do
cat second.csv | grep $line > output_file
done
please help. Thank you
Your question is not entirely clear, but here is what I think you want:
cat first.csv | while read LINE; do
VAL=`echo "$LINE" | cut -d, -f6`
grep -q "$VAL" second.csv && echo $LINE
done
The first line in the loop extracts the 6th field from the line and stores it in VAL. The next line checks (quietly), if VAL occurs in second.csv and if so, outputs the line.
Note that grep will check for any occurence in second.csv, not only in field 2. To check only against field 2, change it to:
cut -d, -f2 second.csv | grep -q "$VAL" && echo $LINE
Unrelated to your question I would like to comment, that those things can much more efficiency be solved in a language like python.
Well... If you have bash with process substitution you can treat all the 2nd fields in second.csv (with a $ appended to anchor the search at the end of the line) as input from a file. Then using grep -f match data from the 2nd column of second.csv with the end of the line in first.csv doing what you intend.
You can use the <(process) form to redirect the 2nd field as a file using:
grep -f <(awk -F, '{print $2"$"}' second.csv) first.csv
Example Output
With the data you show in first.csv and second.csv you get:
1,0,3210820,0,536,7855712
Adding the "$" anchor as part of the 2nd field from second.csv should satisfy the match only in the 6th field (end of line) in first.csv.
The benefit here being there is but a single call to grep and awk, not an additional subshell spawned per-iteration. Doesn't matter with small files like your sample input, but with millions of lines, we are talking hours (or days) of processing time difference.
Lets say I have a folder of .txt files that have a dd-MM-yyyy_HH-mm-ss time followed by _name.txt. I want to be able to sort by name first then time after. Example:
BEFORE
15-2-2010_10-01-55_greg.txt
10-2-1999_10-01-55_greg.txt
10-2-1999_10-01-55_jason.txt
AFTER
greg_1_10-2-1999_10-01-55
greg_2_15-2-2010_10-01-55
jason_1_10-2-1999_10-01-55
Edit: Apologies, from my "cp" line I was meant to copy them into another directory with a different name to them.
Something I tried to do is make a copy with the count, but it doesn't sort the files with the same name properly in terms of dates:
cd data/unfilteredNames
for filename in *.txt; do
n=${filename%.*}
n=${filename##*_}
filteredName=${n%.*}
count=0
find . -type f -name "*_$n" | while read name; do
count=$(($count+1))
cp -p $name ../filteredNames/"$filteredName"_"$count"
done
done
Not sure that the renaming of files is one of your expectation. At least for only sorting file name, you don't need to.
You can do this by only using GNU sort command:
sort -t- -k5.4 -k3.1,3.4 -k2.1,2.1 -k1.1,1.2 -k3.6,3.13 <(printf "%s\n" *.txt)
-t sets the field separator to a dash -.
-k enables to sort based on fields. As explained in man sort page, the syntax is -k<start>,<stop> where <start> or is composed of <field number>.<position>. Adding several -k option to the command allows to sort on multiple fields; the first in he command line having more precedence than the other.
For example, the first -k5.4 tells to sort based on the 5th fields with an offset of 4 characters. There isn't a stop field because this is the end of the filename.
The -k3.1,3.4 option sorts based on the 3rd field starting from offset 1 to 4.
The same principle applies to other -k options.
In your example the month field only has 1 digit. If you have files with a month coded with 2 digits, you might want to pad with 0 all month filenames. This can be done by adding to the printf statement this <(... | sed 's/-0\?\([0-9]\)/-0\1/') and change the -k 2.1,2.1 by -k2.1,2.2.
I've been trying to get my head round this for a few hours now and thought I'd come here and ask for help.
I have a CSV file of IP addresses from a log file that I want to run through and get a WHOIS netrange and company name from and then append the result to the end of the CSV.
So far what I have managed to do is get the whois results to a separate csv
echo ip, company, > result.csv
for ip in $(grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" source.csv); do
whois $ip | grep -i -e 'netrange\|inetnum' -e 'org-name\|orgname' \
| awk 'BEGIN{FS="NetRange:|inetnum:|OrgName:|org-name:"} {print $2","$3}'
|xargs; done >> result.csv
my challenge is how to add my 2 new columns back into the source.csv? I have tried using
paste -d, source.csv result.csv
but all that happens is the values in result.csv overwrite the first few columns of source.csv
my source.csv looks something like the below
ip address requests number of visits
66.249.90.77 2149 200
66.249.66.1 216 233
My result.csv
ip range company
66.249.64.0 - 66.249.95.255 Google Inc.
66.249.64.0 - 66.249.95.255 Google Inc.
i would like my final csv to look like
ip requests number of visits ip range company
66.249.90.77 2149 200 66.249.64.0 - 66.249.95.255 Google Inc.
66.249.66.1 2161 233 66.249.64.0 - 66.249.95.255 Google Inc.
If possible I would prefer to accomplish this with BASH rather than installing any 3rd party tools etc. I have tried the python package ipwhois but my python knowledge is far less than my limited BASH knowledge so I abandoned it lest I continue wasting time!
Any help is much appreciated.
Try putting this in a script file and running it while inside the directory containing your data file.
#!/bin/bash -ue
# Pattern for spacing characters
sp="[[:space:]]*"
#Pattern for non-spacing characters
nsp="[^[:space:]]+"
# Iterate on each line in the data file
while IFS= read -r line
do
[[ "$line" =~ ^$sp($nsp)$sp($nsp)$sp($nsp)$sp$ ]] || continue
f1="${BASH_REMATCH[1]}"
f2="${BASH_REMATCH[2]}"
f3="${BASH_REMATCH[3]}"
# Extract information from whois
whois_data="$(whois "$f1")"
range="$(grep NetRange <<<"$whois_data" | cut -f2 -d":")"
company="$(grep OrgName <<<"$whois_data" | cut -f2 -d":")"
echo $f1,$f2,$f3,$range,$company
done <"source.csv"
The output is formatted as fields separated by commas, and there should be some trimming of the range and company variables before they are used (to remove spaces at the beginning and end), but this should give you the idea.
Basically, the code does not try to merge two files together, but rather extracts the fields from each line of the source file, performs the whois, extracts the two fields needed from it, and then outputs all five fields to the output without an intermediary step.
I have several header files in a directory with the format imageN.hd where N is some integer. Only one of these header files contains the text 'trans'. What I am trying to do is find which image contains this expression using csh (I need to use csh for this purpose - although I can call sed or perl one-liners) and show the corresponding image.
show iN
Here is my initial unsophisticated approach which does not work.
#find number of header files in directory
set n_images = `ls | grep 'image[0-9]*.hd' | wc -l`
foreach N(`seq 1 n_images`)
if (`more image$N{.hd} | grep -i 'trans`) then
show i$N
sc c image #this command uses an alias to set the displayed image as current within the script
endif
end
I'm not sure what is wrong with the above commands but it does not return the correct image number.
Also I'm sure there is a more elegant one line perl or sed solution but I am fairly unfamiliar with both
show `grep -l trans image[0-9]*.hd | sed 's/image/i/`
I have a bunch of files which contain an ascii header with a time stamp WITHIN the file, followed by a large chunck of binary data. I would like to list the files sorted by this time stamp, at the command line (bash, etc).
The file headers look similar to the following:
encoding: raw
endian: big
dimension: 4
sizes: 128 128 1 4
spacings: 1.0 1.0 1.0 NaN
position: -3164,-13678
date_time: 06.02.12.18:59
user_name: Operator1
sample_name:
dwell_time: 4.000
count_time: 65.536
duration: 202.000
raster: 79912
pixel_width: 624.3125
pixel_height: 624.3125
....binary data....
I would like to sort based on the "date_time" time stamp, which uses the format dd.mm.yy.hh:mm
The sort --key option looks promising but all my attempts have failed. Any help is much appreciated. Thanks.
Assuming these files are images, so you can use a tool like exiftool to rename them based on their creation dates, then sort them by name.
If you can't rename them, just dump the names with the creation date to STDOUT and sort that, e.g.:
exiftool -p '$dateTimeOriginal $filename' -q -f DIRECTORY/WHERE/IMAGES/ARE | sort -n
If you only want the filenames in the output, append a | cut -f 2 -d " " to the end.
If it's file format which is not recognized by exiftool this might or might not work:
for f in YOURFILES* ; do
filedate=`grep --binary-file=text -i -o 'date_time: ...........:..' $f | head -1`
echo "$filedate $f"
done | sort -n
Note: this won't work when there are spaces in the filenames (and I'm leaving that to you to resolve). And if you want to output only the sorted filenames, append | awk '{print $NF}' after the sort -n.