BASH - String output is not what I expected - bash

I have a situation where I need to echo:
a variable - plain text - the result of a function call
Im so confused about what the various symbols mean when echoing
Here are my functions:
#!/bin/bash
getLogTimestamp() {
echo $(date +%Y-%m-%dT%H:%M:%S.130Z)
}
getDisplayName(){
echo $(awk -F'=' '/^CARBON_LITE_PLAYER_ID=.*/ { print $2;}' /home/benja/app/.env)
}
getDeviceIPAddress(){
echo $(hostname -i)
}
getSystemUptime(){
echo $(uptime)
}
getDateTimeInfo(){
echo "$(timedatectl status | awk '/Warning/{exit} 1')"
}
getMemoryInfo() {
arr=( $(free -h) )
keys=("${arr[#]::8}")
vals=("${arr[#]:8:12}")
for i in ${!keys[#]}; { printf -v data[i] "%s: %s, " "${keys[i]}" "${vals[i]}"; }
data="${data[#]}"
echo ${data%,*}
}
getDiskInfo()
{
arr=($(df -h))
keys=("${arr[#]::6}")
vals=("${arr[#]:7:100}")
for i in ${!keys[#]}; { printf -v data[i] "%s: %s, " "${keys[i]}" "${vals[i]}"; }
data="${data[#]}"
echo ${data%,*}
}
getDisplayState(){
STATE=$(/opt/vc/bin/tvservice -s | awk '/state/ {print $2}')
case $STATE in
$STATE=0x40001 )
echo VC_SDTV_NTSC, VC_HDMI_UNPLUGGED
;;
$STATE=0x40002 )
echo VC_SDTV_NTSC, VC_HDMI_ATTACHED
;;
$STATE=0x120002 )
echo VC_SDTV_ATTACHED, VC_SDTV_CP_INACTIVE, VC_HDMI_ATTACHED
;;
$STATE=0x120005 )
echo VC_SDTV_ATTACHED, VC_SDTV_CP_INACTIVE, VC_HDMI_UNPLUGGED, VC_HDMI_DVI
;;
$STATE=0x120016 )
echo VC_SDTV_ATTACHED, VC_SDTV_CP_INACTIVE, VC_HDMI_ATTACHED, VC_HDMI_DVI, VC_HDMI_HDCP_UNAUTH
;;
$STATE=0x12001a )
echo VC_SDTV_ATTACHED, VC_SDTV_CP_INACTIVE, VC_HDMI_ATTACHED, VC_HDMI_HDMI, VC_HDMI_HDCP_UNAUTH
;;
$STATE=0x12001a )
echo VC_SDTV_ATTACHED, VC_SDTV_CP_INACTIVE, VC_HDMI_ATTACHED, VC_HDMI_HDMI
;;
$STATE=0x120009 )
echo VC_SDTV_ATTACHED, VC_SDTV_CP_INACTIVE, VC_HDMI_UNPLUGGED, VC_HDMI_HDMI
;;
esac
}
In a separate file I am calling these functions:
#Logging
. /home/benja/app/scripts/log-helper-functions.sh
LOGTIMESTAMP=getLogTimestamp
# RSH -We are logging this data every time this script runs (chron job)
echo $LOGTIMESTAMP "Display Name: $(getDisplayName)"
echo $LOGTIMESTAMP "Device IP: $(getDeviceIPAddress)"
echo $LOGTIMESTAMP "System UpTime: $(getSystemUptime)"
echo $LOGTIMESTAMP "DateTime Info: $(getDateTimeInfo)"
echo $LOGTIMESTAMP "Memory Info: $(getMemoryInfo)"
echo $LOGTIMESTAMP "Disk Info: $(getDiskInfo)"
echo $LOGTIMESTAMP "Display State: $(getDisplayState)"
# RSH end
This is an example of the output:
getLogTimestamp Display Name: UAT-101429
getLogTimestamp Device IP: 10.0.0.120
getLogTimestamp System UpTime: 23:26:06 up 19:54, 1 user, load average: 1.66, 0.82, 0.47
getLogTimestamp DateTime Info: Local time: Thu 2020-04-09 23:26:06 EDT
Universal time: Fri 2020-04-10 03:26:06 UTC
RTC time: n/a
Time zone: America/New_York (EDT, -0400)
Network time on: yes
NTP synchronized: no
RTC in local TZ: yes
getLogTimestamp Memory Info: total: 174M, used: 42M, free: 91M, shared: 388M, buff/cache: 317M, available: Swap:, Mem:: 0B, 605M: 0B
getLogTimestamp Disk Info: Filesystem: /dev/root, Size: 30G, Used: 4.6G, Avail: 23G, Use%: 17%, Mounted: /
getLogTimestamp Display State:
Problems: Obviously the $TIMESTAMP is not resolving, and the Display State is not resolving.
What am I doing wrong?
TIA
-Ron

The right hand side of an assignment doesn't normally run. You need to tell bash to run it and use the output:
LOGTIMESTAMP=$(getLogTimestamp)
The case statement has a different syntax. Each case consists of just the value, you already stated the $STATE in the case $STATE line, no need to repeat it on each line.
case "$STATE" of
0x40001) ...

Related

How do I loop through elements in two bash arrays at the same time

I have a bash script that should execute mongodump on multiple databases within a single mongo instance.
The script should dynamically inject the Mongo-URIs and backup filenames as below :
mongodump --uri=$endpoint --authenticationDatabase admin --gzip --archive=/tmp/$filename.gz
When the script runs its facing an error - it appears its not injecting the variables correctly :
dump.sh
Starting-BACKUP
dump.sh
platforms-2022-10-25-19:05:20
sports-2022-10-25-19:05:20
mongodb://root:mypassword#mongodb-prom/platforms
mongodb://root:mypassword#mongodb-prom/sports
2022-10-25T19:05:20.392+0000 Failed: error creating intents to dump: error getting collections for database `platformsmongodb://root:mypassword#mongodb-prom/sports`: (InvalidNamespace) Invalid database name: 'platformsmongodb://root:mypassword#mongodb-prom/sports'
2022-10-25T19:05:50.409+0000 Failed: can't create session: could not connect to server: server selection error: server selection timeout, current topology: { Type: Single, Servers: [{ Addr: localhost:27017, Type: Unknown, Last error: connection() error occurred during connection handshake: dial tcp [::1]:27017: connect: connection refused }, ] }
Database backup was successful
Both the Mongo-URIs and backup filenames are being populated from arrays as below :
#declare -a BACKUP_NAMES=()
#declare -a BACKUP_ENDPOINTS=()
#declare –a DATABASES=()
#declare –a RAW_DBNAMES=()
#declare –a DATABASES=()
DATABASES+=("platforms")
DATABASES+=("sports")
BACKUP_RETURN_CODE=${?}
MONGODB_URI="mongodb://root:mypassword#mongodb-prom/"
NOW="$(date +"%F")-$(date +"%T")"
#array for raw db names
for DATABASE in "${DATABASES[#]}";
do
RAW_DBNAMES+=("$DATABASE")
done
#array for constructing backup filenames
for DATABASE in "${DATABASES[#]}";
do
#echo $DATABASE
BACKUP_NAMES+=("$DATABASE")
done
#construct backup filenames
cnt=${#BACKUP_NAMES[#]}
for ((i=0;i<cnt;i++)); do
BACKUP_NAMES[i]="${BACKUP_NAMES[i]}-$NOW"
echo "${BACKUP_NAMES[i]}"
done
#construct db endpoints
for ((i=0;i<cnt;i++)); do
uri="$MONGODB_URI${RAW_DBNAMES[i]}"
echo "$uri"
BACKUP_ENDPOINTS+=$uri
done
#pass BACKUP_ENDPOINTS & BACKUP_FILENAMES to mongodump
for ((i=0; i<${#BACKUP_NAMES[#]}; i++));
do
mongodump --uri="${BACKUP_ENDPOINTS[$i]}" --authenticationDatabase admin --gzip --archive=/tmp/"${BACKUP_NAMES[$i]}".gz
done
#If the return code is non-zero then the backup was not successful
if [[ 0 != ${BACKUP_RETURN_CODE} ]]
then
echo "Database backup has failed"
exit ${BACKUP_RETURN_CODE}
else
echo "Database backup was successful"
fi
exit 0
As a check I used these two sample arrays and I have verified that I can indeed loop through two arrays at the same time although this particular example is concatenating the elements (Is this how it works in general?):
array1=( 1 2 3 )
array2=("toronto""new york")
for ((i=0; i<${#array1[#]}; i++));
do echo "${array1[$i]}${array2[$i]}";
done
but I am not sure why in my case the strings are concatenating so wrongly.
What am I missing ?
You don't need multiple arrays create just one and rearrange it like this:
arr=(
# id item product
1 apple juice
2 banana smuzi
3 'raw potato' chips
# ...
)
N=${#arr[*]} # number of items in array
C=3 # number of 'columns'
Then loop over it like so:
for ((i=0; i<$N; i+=$C)); {
read id item product <<< "${arr[#]:$i:$C}"
echo "id: $id"
echo "item: $item"
echo "product: $product"
echo '-----------------'
}
Result:
id: 1
item: apple
product: juice
-----------------
id: 2
item: banana
product: smuzi
-----------------
id: 3
item: raw
product: potato chips
-----------------
p.s. check out this backup script for example it's for psql but probably could be useful.
Yes, it turned i was instantiating the BACKUP_ENDPOINTS array the wrong way.
This is the correct way :
#construct db endpoints
for ((i=0;i<cnt;i++)); do
# uri="$MONGODB_URI${RAW_DBNAMES[i]}"
# echo "$uri"
# BACKUP_ENDPOINTS+=$uri THIS IS WRONG !!!
BACKUP_ENDPOINTS[i]="$MONGODB_URI${RAW_DBNAMES[i]}"
done
for ENDPOINT in "${BACKUP_ENDPOINTS[#]}"; do
echo "$ENDPOINT"
done
#pass BACKUP_ENDPOINTS & BACKUP_FILENAMES to mongodump
for ((i=0; i<${#BACKUP_NAMES[#]}; i++));
do
mongodump --uri="${BACKUP_ENDPOINTS[$i]}" --authenticationDatabase admin --gzip --archive=/tmp/"${BACKUP_NAMES[$i]}".gz
done

Shell Function to get value of a property from YAML file

I am trying to create a unix shell/bash function to get a specific value from a YAML file.
I don't want to use Python/Ruby or other scripting languages or do not want to install any packages that are not natively available in bash.
The function should take the property name name and yaml file name as input and return the value of the property.
Sample YAML is given below.
---
Action: start
Version: 642
Domains:
Domain:
Name: SanityTest
AppSpaces:
AppSpace:
Name: SanityAppSpace
AppNodes:
AppNode:
Name: SanityAppnode
Applications:
Application:
Name: InstagramTest.application
Version: "1.0"
AppNode:
Name: SanityAppnode_1
Applications:
Application:
Name: InstagramTest.application
Version: "1.0"
I am looking for some function that works like:
DomainName=getValue Domains_Domain[1]_Name /path/my_yaml.yml
AppSpaceName=getValue Domains_Domain[$DomainName]_AppSpaces_AppSpace[1]_Name /path/my_yaml.yml
AppNodeName=getValue Domains_Domain[$DomainName]_AppSpaces_AppSpace[$AppSpaceName]_AppNodes_AppNode[1]_Name /path/my_yaml.yml
AppName=getValue Domains_Domain[$DomainName]_AppSpaces_AppSpace[$AppSpaceName]_AppNodes_AppNode[$AppNodeName]_Applications_Application[1]_Name /path/my_yaml.yml
AppVersion=getValue Domains_Domain[$DomainName]_AppSpaces_AppSpace[$AppSpaceName]_AppNodes_AppNode[$AppNodeName]_Applications_Application[$AppName]_Version /path/my_yaml.yml
I came up with the below code so far, but it is not working as expected. Appreciate any help. Please advise if my approach is wrong and if there is better way.
function getValue {
local ymlFile=$2
local Property=$1
IFS='_' read -r -a props_tokens <<< "${Property}"
local props_index=0
echo "${props_tokens[props_index]}"
CheckingFor="Domains"
currEntity_Index=0
while IFS= read -r line; do
curr_prop="${props_tokens[props_index]}"
curr_prop_Index=0
IFS=':' read -r -a curr_line_props <<< "${line}"
curr_line_prop = ${curr_line_props[0]}
if [ "${props_tokens[props_index]}" == *"["*"]"* ]
then
IFS='[' read -r -a prop_tokens <<< "${props_tokens[props_index]}"
curr_prop=${prop_tokens[0]}
curr_prop_Index=${prop_tokens[1]::-1}
echo "Index processed"
echo $curr_prop
echo $curr_prop_Index
else echo "No Index for this property"
fi
if [ "$curr_line_prop" != "$CheckingFor" ]
then continue
fi
if [ "|Action|Name|Version|" == *"$curr_prop"* ]
then
return curr_line_props[1]
fi
if [ "$curr_prop" == "$CheckingFor" ]
then
if [ "|Domains|AppSpaces|AppNodes|Applications|" == *"$curr_prop"* ]
then
props_index++
case $CheckingFor in
Domains)
CheckingFor="Domain";;
Domain)
CheckingFor="AppSpaces";;
AppSpaces)
CheckingFor="AppSpace";;
AppSpace)
CheckingFor="AppNodes";;
AppNodes)
CheckingFor="AppNode";;
AppNode)
CheckingFor="Applications";;
Applications)
CheckingFor="Application";;
*) echo "$curr_prop - not switched";;
esac
continue
else
if [ "$curr_prop_Index" == 0 ]
then
case $CheckingFor in
Domains)
CheckingFor="Domain";;
Domain)
CheckingFor="AppSpaces";;
AppSpaces)
CheckingFor="AppSpace";;
AppSpace)
CheckingFor="AppNodes";;
AppNodes)
CheckingFor="AppNode";;
AppNode)
CheckingFor="Applications";;
Applications)
CheckingFor="Application";;
*) echo "$curr_prop - not switched";;
esac
props_index++
continue
else
Integer_regex='^[0-9]+$'
if [[ $curr_prop_Index =~ $Integer_regex ]]
then
# Iterate until we get to the nth Item
currEntity_Index++
if [ $currEntity_Index == $curr_prop_Index ]
then
case $CheckingFor in
Domains)
CheckingFor="Domain";;
Domain)
CheckingFor="AppSpaces";;
AppSpaces)
CheckingFor="AppSpace";;
AppSpace)
CheckingFor="AppNodes";;
AppNodes)
CheckingFor="AppNode";;
AppNode)
CheckingFor="Applications";;
Applications)
CheckingFor="Application";;
*) echo "$curr_prop - not switched";;
esac
currEntity_Index=0
else
fi
props_index++
continue
else
# Iterate until the name of the entity match and continue with next property token
# How to handle if the search sipllied into next object?
fi
fi
fi
else
props_index++
continue
fi
done < $ymlFile
return "${props_tokens[props_index]}"
}

bash script delete mails from specific address (pop3 account)

I want to clean my mailbox from mails from specific address
I have thousands of messages, I want to do this in bash script, and run it from time to time (a receive SPAM from different addresses, and unfortunately my "spam filters" have only small effect on them)
To interact with a mail server through command line, you could use either telnet or openssl.
You can connect to your pop server using the following command (I've taken gmail as an example. You'll have to look for your email host pop3 address and socket.) :
openssl s_client -connect pop.gmail.com:995 -quiet
As this command is interactive, it will ask for a username, a password and a serie of commands.
expect is a tool that can automate interaction with interactive commands. The basic syntax is as follow : expect "somestring" action -> If the program we monitor displays "somestring", we execute the action.
Here is a script that would delete all the messages present on your email address :
#!/usr/bin/expect
#you can modify the timeout if the script fails
set timeout 1
#our connection variables
set ip "pop.gmail.com"
set socket "995"
set user "user.name"
set pass "password"
#we set the address we want to remove mails from here. Escape special regex characters such as dots.
set target_address "mail\.example#gmail\.com"
#we launch the subprocess we want to interact with
spawn openssl s_client -connect $ip:$socket -quiet
#if connection went all right, we try to login
expect -re ".OK.*" {send "user $user\r"}
expect -re ".OK.*" {send "pass $pass\r"}
#if login went alright, we try to count the messages on the server
#you will get the following output :
#+OK NB_MSG TOTAL_SIZE
expect -re ".OK.*" {send "stat\r"}
#if the stat command went alright ...
expect -re ".OK.*" {
#we extract the number of mail from the output of the stat command
set mail_count [lindex [split [lindex [split $expect_out(buffer) \n] 1] " "] 1]
#we iterate through every email...
for {set i 1} {$i <= $mail_count} {incr i 1} {
#we retrieve the header of the email
send "top $i 0\r"
#if the header contains "To: $target_address" (or "To: <$target_address>" or "To: Contact Name <$target_address>" ...)
#to filter according to the sender, change the regex to "\nFrom: ..."
expect -re "\nTo: \[^\n\]*$target_address" {
#we delete the email
send "dele $i\r"
}
}
}
expect default
You might need to alter your email account settings to allow the use of external programs
Here I have written a similar script in PHP, by use of the IMAP function of PHP. I did not have Linux, so I could not use the bash script. However. I thought that sharing the PHP-script would provide a solution for those running WINDOWS and having access
<?php
echo "My showmail.php script";
// This is the format for opening the email connection:
//
// domain prt foldr[.<.subfolder........>] my#blp.com xxxxxxxx
// $connection = imap_open('{imap.one.com:143}Inbox.<write subfolder here>','email-name','password');
// Execution: (write this in the browsers address bar)
http://digitalageinstitute.com/showmail.php?fromdate=01%20January%202009&todate=28%20August%202018&search=#weheartit.com&email=david.svarrer#digitalageinstitute.com&password=xxxxxxx&domain=imap.one.com&folder=Inbox
//
// This line above, entered into a browsers address bar will execute this script (named showmail.php),
// from the servers root (www.digitalageinstitute.com), and
//
// will delete emails from the mail folder "Inbox" (case sensitive) for the user david.svarrer#digitalageinstitute.com, by use of
// the IMAP domain (this is where you access emails from outside) imap.one.com and at port 143 (see this, further down)
//
$emailaddress = $_GET["email"];
$emailpassword= $_GET["password"];
$fromdate = $_GET["fromdate"];
$todate = $_GET["todate"];
$search = $_GET["search"];
$domain = $_GET["domain"];
$folder = $_GET["folder"];
echo $emailaddress." ".$emailpassword." ".$fromdate." ".$todate." ".$search." ".$domain." ".$folder;
$connection = imap_open('{'.$domain.':143}'.$folder,$emailaddress,$emailpassword);
$ccount = imap_num_msg($connection);
$maxtoexpunge=20000;
echo "Parameters = ".$fromdate.",".$todate.",".$search.":".'ALL FROM "'.$search.'" SINCE "'.$fromdate.'" BEFORE "'.$todate.'"';
// $some = imap_search($connection, 'ALL FROM "Twitter"');
$some = imap_search($connection, 'ALL FROM "'.$search.'" SINCE "'.$fromdate.'" BEFORE "'.$todate.'"');
print_r ($some);
echo "<br/>Elements: ".count($some)."<br/>";
$expunge = 0;
for($msgno = 0; $msgno < count($some) ; $msgno++) {
// echo "Fetching element ".$some[$msgno]."<br/>";
$headers = imap_headerinfo($connection, $some[$msgno]);
//$position = True;
$position = strpos(" ".strtolower($headers->fromaddress), strtolower($search));
$position2 = strpos(" ".strtolower($headers->subject), strtolower($search));
if ($position || $position2) {
if ($expunge<$maxtoexpunge) {
imap_delete ($connection, $some[$msgno]);
echo "<br/>Deleting:".$expunge." sequence [".$msgno."]=".$some[$msgno]." ".$headers->fromaddress.", subject:".$headers->subject;
$expunge++;
} else {
echo "<br/>Skipping:"." sequence [".$msgno."]=".$some[$msgno]." ".$headers->fromaddress.", subject:".$headers->subject;
}
}
}
// The expunge command deletes after all action has been taken. DON'T call it until very end, as it will otherwise mess up message numbers!!
imap_expunge ($connection);
echo "<br/>Expunged!!<br/>";
/* Here are the keywords to be used for the imap search order.
toaddress - full to: line, up to 1024 characters
to - an array of objects from the To: line, with the following properties: personal, adl, mailbox, and host
fromaddress - full from: line, up to 1024 characters
from - an array of objects from the From: line, with the following properties: personal, adl, mailbox, and host
ccaddress - full cc: line, up to 1024 characters
cc - an array of objects from the Cc: line, with the following properties: personal, adl, mailbox, and host
bccaddress - full bcc: line, up to 1024 characters
bcc - an array of objects from the Bcc: line, with the following properties: personal, adl, mailbox, and host
reply_toaddress - full Reply-To: line, up to 1024 characters
reply_to - an array of objects from the Reply-To: line, with the following properties: personal, adl, mailbox, and host
senderaddress - full sender: line, up to 1024 characters
sender - an array of objects from the Sender: line, with the following properties: personal, adl, mailbox, and host
return_pathaddress - full Return-Path: line, up to 1024 characters
return_path - an array of objects from the Return-Path: line, with the following properties: personal, adl, mailbox, and host
remail -
date - The message date as found in its headers
Date - Same as date
subject - The message subject
Subject - Same as subject
in_reply_to -
message_id -
newsgroups -
followup_to -
references -
Recent - R if recent and seen, N if recent and not seen, ' ' if not recent.
Unseen - U if not seen AND not recent, ' ' if seen OR not seen and recent
Flagged - F if flagged, ' ' if not flagged
Answered - A if answered, ' ' if unanswered
Deleted - D if deleted, ' ' if not deleted
Draft - X if draft, ' ' if not draft
Msgno - The message number
MailDate -
Size - The message size
udate - mail message date in Unix time
fetchfrom - from line formatted to fit fromlength characters
fetchsubject - subject line formatted to fit subjectlength characters
*/
?>
I was looking for a bash script and as I did not found one I made my own, only external programm needed is socat for SSL/TLS connect to server:
#!/bin/bash -
#===============================================================================
#
# FILE: delete-mail.sh
#
USAGE="./delete-mail.sh user pass [server] [port] [max_delete]"
#
# PARAMETER: user pass - login credentials, mandatory
# server - mail server, default: imap.1und1.de
# port - port on mail server, default: pop3s
# max_delete - maximum # of messages to delete, default: 1000
#
DESCRIPTION="delete max_delete messages in given pop3 mailbox"
#
# AUTHOR: KayM (), kay#rrr.de
# CREATED: 09.06.2021 18:03:58
# REVISION: ---
#===============================================================================
# check args
if [[ "$1" == "-h"* ]]; then
printf "usage: %s\ndescr: %s\n" "$USAGE" "$DESCRIPTION" 1>&2
exit
elif [ "$#" -lt 2 ]; then
printf "Missing args, usage: %s\n" "$USAGE"
exit 1
fi
# assign values
USER="$1"
PASS="$2"
SERVER="${3:-imap.1und1.de}"
PORT="${4:-pop3s}"
MAX_MESSAGE="${5:-100}"
# define actions
popserver() { socat - "OPENSSL:$SERVER:$PORT" 2>/dev/null; }
poplogin() {
printf "USER %s\n" "$USER"
sleep 0.5
printf "PASS %s\n" "$PASS"
sleep 0.5
}
deletemsg() {
for (( j = 1 ; j <= MAX_MESSAGE; j++ ))
do
printf "DELE %s\n" "$j"
sleep 0.5
done
}
popstat() { echo "STAT"; }
popquit() { echo "QUIT"; }
checkresult() {
local line
while read -r line
do
[[ "$line" == "-ERR "* ]] && MAX_MESSAGE=0
printf "%s\n" "$line"
done
}
# get message count, remove newline
RES="$({ poplogin; popstat; } | popserver | tail -n 1 )"
RES="${RES//[$'\r\n']}"
if [[ "$RES" == "+OK 0 "* ]]; then
printf "No messages to delete (%s)\n" "$RES"
exit
elif [[ "$RES" == "+OK "* ]]; then
: "${RES#+OK }"; NUM="${_%% *}"
printf "%s Messages found (%s)\n" "$NUM" "$RES"
[ "$NUM" -lt "$MAX_MESSAGE" ] && MAX_MESSAGE="$NUM"
else
printf "Can't get number of messages: %s\n" "$RES"
exit 1
fi
if [ "$MAX_MESSAGE" = "0" ]; then
printf "Keep all messages\n"
exit
fi
# delete messages
printf "Delete %s messages ...\n" "$MAX_MESSAGE"
{ poplogin; deletemsg; popstat; popquit;} | popserver | checkresult

Waiting until all processes end to execute next line in Bash

In a script I'm writing right now, I create many background processes in attempts to run my script on multiple devices in parallel. This functionality works, but it would appear I have no control of it. The simple wait command does not get me the results I need.
Abridged code:
#!/bin/bash
echo ""
date
echo ""
echo "Displaying devices to be configured:"
./adb devices | sed "1d ; $ d"
echo ""
echo "###########################"
echo "# #"
echo "# Starting configuration! #"
echo "# #"
echo "###########################"
echo ""
# All commands ran through this function
DeviceConfig () {
...
# Large list of commands
...
}
# This is the loop that spawns all the processes. Note the ampersand I'm using.
for usb in $(./adb devices -l | awk '/ device usb:/{print $3}'); do ( DeviceConfig & ) ; done
echo ""
echo "###########################"
echo "# #"
echo "# Configuration complete! #"
echo "# #"
echo "###########################"
While this will successfully run all my commands in parallel, my output is not as intended.
Actual output:
Wed Oct 5 13:11:26 EDT 2016
Displaying devices to be configured:
3100c2759da2a200 device
3100c2ddbbafa200 device
###########################
# #
# Starting configuration! #
# #
###########################
###########################
# #
# Configuration complete! #
# #
###########################
Starting: Intent { cmp=com.android.settings/.Settings }
Warning: Activity not started, its current task has been brought to the front
Starting: Intent { cmp=com.android.settings/.Settings }
Warning: Activity not started, its current task has been brought to the front
...
(The ... is to imply more output from the script.)
Putting a wait in the loop does not solve the issue. Putting a wait after the loop does not solve the issue. How do I write this loop so the configurations happen in between the Starting configuration! and Configuration complete! output?
You can ask wait to wait on multiple processes, e.g.:
pids=()
for usb in $(./adb devices -l | awk '/ device usb:/{print $3}'); do DeviceConfig & pids+=($!); done
wait "${pids[#]}"

exiftools change hour from alphabetical filename order

I want to change the hours all all jpeg files, already sorted by alphabetical order.
I sort my album by hand, then I rename the files in alphabetical order. But, If I push this file in Google Photo, the order is remove.
So, I want to change only the time incrementally to respect the alphabetical order, the order of my album.
I work with Ubuntu.
Any idea ?
Here is a little bash script that should get you started. It processes all the JPG/JPEG files in the current directory, one at a time using jhead. It extracts the date/time from the file and checks if it is a different day from the previous file. If it is a new day, it starts the hours, minutes and seconds at 0, i.e. midnight on the new day. If it is the same day as the previous file, it increments the seconds from the previous file - with overflow into minutes and hours.
That means the first file each day will come out with timestamp midnight, then next one that day will be 00:00:01, the next one will be 00:00:02 and so on. Of course, you can change the scheme.
#!/bin/bash
# Don't barf if no files or if files named with JPG,jpg,JPEG,jpeg
shopt -s nullglob
shopt -s nocaseglob
# Process files in current name order
for f in *.jpg *jpeg; do
echo Processing $f...
# Get current YMD, HMS in format yyyy:mm:dd and hh:mm:ss
read ymd hms < <( jhead "$f" | awk '/Date/{print $3,$4}')
[ ${#ymd} -lt 10 ] && { echo $f no date found - skipping; continue; }
[ ${#hms} -lt 8 ] && { echo $f no time found - skipping; continue; }
# Check if date has changed
if [ "$ymd" != "$lastymd" ]; then
# New day, reset hours, mins, secs to zero
hrs=0
mins=0
secs=0
else
((secs=secs+1))
[ $secs -eq 60 ] && { ((mins=mins+1)); secs=0; }
[ $mins -eq 60 ] && { ((hrs=hrs+1)); mins=0; }
[ $hrs -gt 23 ] && { echo ERROR: Too many photos today $ymd; exit 1; }
fi
newts=$(printf "%s-%02d:%02d:%02d" $ymd $hrs $mins $secs)
echo File: $f, current time: $ymd,$hms new time: $newts
#jhead -ts"$newts" "$f"
lastymd=$ymd
done
At the moment, it does nothing, but remove the # in front of jhead on the third from last line if you want it to actually modify your files.
PLEASE MAKE A BACKUP FIRST and run on a copy of a few files in a temporary directory.
Sample Output
File: IMG_4045.JPG, current time: 2015:06:21,08:49:34 new time: 2015:06:21-00:00:00
Processing IMG_4046.JPG...
File: IMG_4046.JPG, current time: 2015:06:21,08:49:36 new time: 2015:06:21-00:00:01
Processing IMG_4047.JPG...
File: IMG_4047.JPG, current time: 2015:06:21,08:49:37 new time: 2015:06:21-00:00:02
Processing IMG_4048.JPG...
File: IMG_4048.JPG, current time: 2015:06:21,08:49:39 new time: 2015:06:21-00:00:03
Processing IMG_4049.JPG...
File: IMG_4049.JPG, current time: 2015:06:21,08:49:41 new time: 2015:06:21-00:00:04
Processing IMG_4052.JPG...
File: IMG_4052.JPG, current time: 2015:06:21,15:13:43 new time: 2015:06:21-00:00:00
Processing IMG_4053.JPG...
File: IMG_4053.JPG, current time: 2015:06:21,15:13:45 new time: 2015:06:21-00:00:01
Processing IMG_4054.JPG...
File: IMG_4054.JPG, current time: 2015:06:21,15:22:51 new time: 2015:06:21-00:00:02
The code is very good. I change a little to fixe some bugs or add features.
# Don't barf if no files or if files named with JPG,jpg,JPEG,jpeg
shopt -s nullglob
shopt -s nocaseglob
hrs=0
mins=0
secs=0
# Process files in current name order
for f in *.jpg *.jpeg *.mp4 *.mpg *.mov *.3gp *.gif *.png *.tif; do
echo Processing $f...
# Get current YMD, HMS in format yyyy:mm:dd and hh:mm:ss
read ymd hms < <( jhead "$f" | awk '/Date/{print $3,$4}')
[ "${ymd}" = "" ] && { ymd = "$lastymd"; }
[ ${#ymd} -lt 10 ] && { echo "$f no date found"; ymd="$lastymd"; }
[ ${#hms} -lt 8 ] && { echo "$f no time found"; ymd="$lastymd"; }
# Check if date has changed
if [ "$ymd" != "$lastymd" ]; then
# New day, reset hours, mins, secs to zero
hrs=0
mins=0
secs=0
else
((secs=secs+1))
[ "$secs" -eq 60 ] && { ((mins=mins+1)); secs=0; }
[ "$mins" -eq 60 ] && { ((hrs=hrs+1)); mins=0; }
[ "$hrs" -gt 24 ] && { echo ERROR: Too many photos today $ymd; exit 1; }
fi
newts=$(printf "%s %02d:%02d:%02d" $ymd $hrs $mins $secs)
echo File: $f, current time: $ymd,$hms new time: $newts
exiftool -overwrite_original -m "-alldates=$newts" "$f"
exiftool -overwrite_original -m '-DateTimeOriginal>FileModifyDate' "$f"
lastymd=$ymd
done

Resources