Pass a parameterized folder name to command line - command-line

I use the following command-line:
call run.bat TEST.properties
In TEST.properties file I initialize the following parameter
output.dir=C:/Test_Results
I would like the 'Test_Results' to contain a timestamp each time the script is called. How can I accomplish that? Thank you!

In TEST.properties.bat, after
output.dir=C:\Test_Results
insert the line
echo %date% %time% >>%output.dir%\my_timestamps.txt
and the latest date/time the TEST.properties.bat is run will appear in C:\Test_Results\my_timestamps.txt
Note that / is a switch-indicator. \ is a directory-separator.

If you only need 1 timestamp in 'Test_Results':
set test=%1
rem insert timestamp generating code below if needed
set timestamp=%time%
for /f "tokens=1,2* delims==" %%i in (%test%) do (if "%%i"=="output.dir" echo %timestamp%>%%j)
If you need all the timestamps:
set test=%1
rem insert timestamp generating code below if needed
set timestamp=%time%
for /f "tokens=1,2* delims==" %%i in (%test%) do (
if "%%i"=="output.dir" (
if not exist %%j (echo %timestamp%>%%j) else (
echo %timestamp%>temp.txt
copy %%j+temp.txt %%j
del temp.txt
)
)
)

Related

How to return exit code of now rows matched in postgresql

I am writing a script so that to filter jobs that are failed in past 24hrs and also the failed job is not completed or not in running state again.
rm -rf jobsfailed.txt jobscompleted.txt jobsnotcompleted.txt ## Remove the files which are created
export PGPASSWORD="$PG_PASS"
failed_jobs=`psql -t -U bacula bacula << EOD
SELECT jobid,job,name,jobbytes,realendtime,jobstatus,level FROM job WHERE jobstatus IN ('f','A','E') AND realendtime > NOW() - INTERVAL '24 HOURS';
\q
EOD` ### Collect all the jobs which are in the defined states
echo "$failed_jobs" >> jobsfailed.txt ### redirect the values to a file
sortedlist=$(awk -F'|' '{print $3}' jobsfailed.txt | sort | uniq) ## sort the values based on the jobname
for i in $sortedlist
do
retVal=$?
jobs_notcompleted=`psql -t -U bacula bacula << EOD1
SELECT jobid,job,name,jobbytes,realendtime,jobstatus,level FROM job WHERE name LIKE '$i' AND jobstatus IN ('T','R') AND starttime > NOW() - INTERVAL '24 HOURS' ORDER BY jobid DESC LIMIT 1;
\q
EOD1` ### If the job is in above defined states(T or R) then jobs completed successfully. Any other state apart from above then job not completed
if [[ $retVal -eq 0 ]]; then
echo "$jobs_notcompleted" >> jobscompleted.txt
else
echo "$jobs_notcompleted" >> jobsnotcompleted.txt
fi
exit $retVal
done
But i am not getting desired output. Since if no state is getting matched, then it is producing (0 rows) output. Please let me know, is there any other way if 0 rows are matched then that $jobs_notcompleted value should redirect to the jobsnotcompleted.txt file. jobscompleted.txt file is getting created and working as expected.
For some reason, I feel that you need a "NOT" between "jobstatus" and "in" for your second query, because the ${retVal} would, in my mind, always return 0 unless there was a DB error.
If I am wrong on that point, then the "retVal=$?" needs to be moved to after the 2nd query.

ERROR: invalid input syntax for type timestamp with time zone

I have a PostgreSQL query as below which is running fine . I am calling it from a shell script as below
Result=$(psql -U username -d database -t -c
$'SELECT round(sum(i.total), 2) AS "ROUND(sum(i.total), 2)"
FROM invoice i
WHERE i.create_datetime = '2019-03-01 00:00:00-06'
AND i.is_review = '1' AND i.user_id != 60;')
now I want the value which I have hard coded as i.create_datetime = '2019-03-01 00:00:00-06' to replace it with a variable date value.
I have tried two ways
way 1:
Result=$(psql -U username -d database -t -c
$'WITH var(reviewMonth) as (values(\'$reviewMonth\'))
SELECT round(sum(i.total),2) AS "ROUND(sum(i.total),2)"
FROM var,invoice i
WHERE i.create_datetime = var.reviewMonth::timestamp
AND i.is_review = \'1\' AND i.user_id != 60;')
and
way 2:
Result=$(psql -U username -d database -t -c
$'SELECT round(sum(i.total),2) AS "ROUND(sum(i.total),2)"
FROM invoice i
WHERE i.create_datetime = \'$reviewMonth\'
AND i.is_review = \'1\' AND i.user_id != 60;')
But both way it's throwing error
way 1 throwing error as :
ERROR: operator does not exist: timestamp with time zone = text
way 2 throwing error as :
ERROR: invalid input syntax for type timestamp with time zone: "$reviewMonth"
Please suggest what should be my approach.
You should try using the psql variables. Here's an example:
# Put the query in a file, with the variable TSTAMP:
> echo "SELECT :'TSTAMP'::timestamp with time zone;" > query.sql
> export TSTAMP='2019-03-01 00:00:00-06'
> RESULT=$(psql -U postgres -t --variable=TSTAMP="$TSTAMP" -f query.sql )
> echo $RESULT
2019-03-01 06:00:00+00
Note how we format the string literal substitution in the query: :'TSTAMP'
You could also do the substitution yourself. Here's an example using a heredoc:
> export TSTAMP='2019-03-01 00:00:01-06'
> RESULT=$(psql -U postgres -t << EOF
SELECT '$TSTAMP'::timestamp with time zone;
EOF
)
> echo $RESULT
2019-03-01 06:00:01+00
In this case, we aren't using psql's variable substitution, so we have to quote the variable like '$TSTAMP' . Using a heredoc makes the quoting much simpler than using -c because you aren't trying to quote the whole command.
EDIT: more examples because it appears this wasn't clear enough. TSTAMP does not have to be hard coded, it's just a bash variable than can be set like any other bash variable.
> TSTAMP=$(date -d 'now' +'%Y-%m-01 00:00:00')
> RESULT=$(psql -U postgres -t << EOF
SELECT '$TSTAMP'::timestamp with time zone;
EOF
)
> echo $RESULT
2019-06-01 00:00:00+00
However, if you're really just looking for the start of the month, there's no need for shell variables at all
> RESULT=$(psql -U postgres -t << EOF
SELECT date_trunc('month', now());
EOF
)
> echo $RESULT
2019-06-01 00:00:00+00

Conditionally construct schema depending on database version in PostgreSQL

Assume the following table and custom range type:
create table booking (
identifier integer not null primary key,
room uuid not null,
start_time time without time zone not null,
end_time time without time zone not null
);
create type timerange as range (subtype = time);
In PostgreSQL v10, you can do:
alter table booking add constraint overlapping_times
exclude using gist
(
room with =,
timerange(start_time, end_time) with &&
);
In PostgreSQL v9.5/v9.6, you have to manually cast the uuid column as gist_btree does not support uuid:
alter table booking add constraint overlapping_times
exclude using gist
(
(room::text) with =,
timerange(start_time, end_time) with &&
);
I would like to support v9.5, v9.6 and v10 for my customers. Is there a way to conditionally add the above constraint in the same .sql file, depending on the version of the current database?
You can use dynamic sql
For example:
do
$block$
declare
l_version text;
begin
select setting into l_version from pg_settings where name = 'server_version';
execute
format(
$script$
alter table booking add constraint overlapping_times
exclude using gist
(
%s with =,
timerange(start_time, end_time) with &&
)
$script$,
case when (l_version like '9.5.%' or l_version like '9.6.%') then '(room::text)' else 'room' end
)
;
end;
$block$
language plpgsql;
Here is a proof of concept:
#!/bin/sh
#THE_HOST="192.168.0.104"
THE_HOST="192.168.0.101"
get_version ()
{
psql -t -h ${THE_HOST} -U postgres postgres <<OMG | awk -e '{ print $2; }'
select version();
OMG
}
# ############################################################################
# main
#
# - Connect to database to retrieve version
# - use the retrieved version to create a symlink "versioned" to
# one of our subdirs
# - call an sql script that includes this symlink/some.sql
# symlinking to a non-existing directory will cause a dead link
# (, and the script to fail.)
# ergo: there should be a subdir "verX.Y.Z" for every supported version X.Y.Z
# ############################################################################
pg_version=`get_version`
#echo "version=${pg_version}"
rm versioned
ln -fs "ver${pg_version}" versioned
if [ -f versioned/alter.sql ]; then
echo created link versioned to "ver${pg_version}"
else
echo "version ${pg_version} not supported today..."
echo "Failed!"
exit 1
fi
psql -t -h ${THE_HOST} -U postgres postgres <<LETS_GO
\i common/create.sql
\i versioned/alter.sql
\echo done!
LETS_GO
#eof

Remove any text between two parameters not working properly

I need to remove any data between , and ( and the "," along with it.
I'm currently using the below command.
sed -i '/,/,/(/{//!d;s/ ,$//}' test1.txt
cat test1.txt
CREATE SET TABLE EDW_EXTRC_TAB.AVER_MED_CLM_HDR_EXTRC
,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
EXTRC_RUN_ID INTEGER NOT NULL,
Current Output
CREATE SET TABLE EDW_EXTRC_TAB.AVER_MED_CLM_HDR_EXTRC
,NO FALLBACK (
EXTRC_RUN_ID INTEGER NOT NULL,
Expected Output:
CREATE SET TABLE EDW_EXTRC_TAB.AVER_MED_CLM_HDR_EXTRC
(
EXTRC_RUN_ID INTEGER NOT NULL,
What is wrong here ?
Any suggestions?
Thanks in advance.
Two approaches:
-- GNU sed approach:
sed -z 's/,[^(]*//' test1.txt
-- GNU awk approach:
awk -v RS= '{ sub(/,[^(]+/,"",$0) }1' test1.txt
The output:
CREATE SET TABLE EDW_EXTRC_TAB.AVER_MED_CLM_HDR_EXTRC
( EXTRC_RUN_ID INTEGER NOT NULL,

Order and move files into directories based on some filenames pattern

To move files into folders I use this script
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "SPLITCHAR=-" & rem // (a single character to split the file names)
set "SEARCHSTR=_" & rem // (a certain string to be replaced by another)
set "REPLACSTR= " & rem // (a string to replace all found search strings)
set "OVERWRITE=" & rem // (set to non-empty value to force overwriting)
rem // Get file location and pattern from command line arguments:
set "LOCATION=%~1" & rem // (directory to move the processed files into)
set "PATTERNS=%~2" & rem // (file pattern; match all files if empty)
rem /* Prepare overwrite flag (if defined, set to character forbidden
rem in file names; this affects later check for file existence): */
if defined OVERWRITE set "OVERWRITE=|"
rem // Continue only if target location is given:
if defined LOCATION (
rem // Create target location (surpress error if it already exists):
2> nul md "%LOCATION%"
rem /* Loop through all files matching the given pattern
rem in the current working directory: */
for /F "eol=| delims=" %%F in ('dir /B "%PATTERNS%"') do (
rem // Process each file in a sub-routine:
call :PROCESS "%%F" "%LOCATION%" "%SPLITCHAR%" "%SEARCHSTR%" "%REPLACSTR%"
)
)
endlocal
exit /B
:PROCESS
rem // Retrieve first argument of sub-routine:
set "FILE=%~1"
rem // Split name at (first) split character and get portion in front:
for /F "delims=%~3" %%E in ("%~1") do (
rem // Append a split character to partial name:
set "FOLDER=%%E%~3"
)
setlocal EnableDelayedExpansion
rem // Right-trim partial name:
if not "%~4"=="" set "FOLDER=!FOLDER:%~4%~3=!"
set "FOLDER=!FOLDER:%~3=!"
rem /* Check whether partial name is not empty
rem (could happen if name began with split character): */
if defined FOLDER (
rem // Replace every search string with another:
if not "%~4"=="" set "FOLDER=!FOLDER:%~4=%~5!"
rem // Create sub-directory (surpress error if it already exists):
2> nul md "%~2\!FOLDER!"
rem /* Check if target file already exists; if overwrite flag is
rem set (to an invalid character), the target cannot exist: */
if not exist "%~2\!FOLDER!\!FILE!%OVERWRITE%" (
rem // Move file finally (surpress `1 file(s) moved.` message):
1> nul move /Y "!FILE!" "%~2\!FOLDER!"
)
)
endlocal
exit /B
To use script I must
1- open cmd
2- to execute batch i have to
cd C:\Users\Administrator\Desktop\T\
"C:\Users\Administrator\Desktop\T\build-folder-hierarchy.bat" "C:\Users\Administrator\Desktop\T\" "*.pdf"
But problem what is ?
For each .pdf file batch creates a relative folder but I don't want it creates folders in that way. Look http://i.imgur.com/TVhQyzv.png
aaaa aaaa S02 [folder]
aaaa aaaa S02e01.pdf [folder]
aaaa aaaa S02e02.pdf [folder]
aaa.aaaa.aaa.aa.aaaaa.S02 [folder]
What I want instead ?
├─aaaa aaaa S02 [folder]
│ ├─aaaa aaaa S02e01.pdf[file]
│ ├─aaaa aaaa S02e02.pdf [file]
└─ ....
├─aaa.aaaa.aaa.aa.aaaaa.S02 [folder]
│ └─aaa.aaaa.aa.aa.aaaaa.S02E13.pdf [file]
:
Just an example name to understand how .pdf files name are formatted
aaaaaaaaa aa aaaaa S01e12 720p Repack.pdf
aaa aaaaaaaaa S01e05 Versione 720p.pdf
aaa aaaaaaaa S01e05 Versione 1080p.pdf
aaa aaaa s2e06.pdf
aaa aaaa S03e12.pdf
aaa.aaaa.aaa.on.Earth.S02E13.pdf
aaa.aaaa.aaaa.S02E01.HDTV.x264.SUB.ITA.pdf
Usually pdf files name are formatted in this way [pattern]
s01
s01e1
s1
s1e1
s1e01
s1e01-10
character, like the e and s are almost always present within these patterns name
general form should be
sxx
sxxex
sx
sxex
sxexx
sxexx-xx
X is a number, case for letter s and e is irrilevant
Powershell solution is well accepted for answer.
Your question is confusing. You have not described the format of the file names, but just show some examples and using examples instead of specifications may be misunderstood. Post code wrote for other problem that don't work on this one is certainly not useful. You did not showed an example of the input and wanted output using real file names, so it is possible that a solution based on the example data will not work on real data.
EDIT: New specification added. Both specifications and program code have been modified accordingly to a request given in comments.
Below there are the specifications of this problem as I understand they:
"Given a series of *.pdf files with this format:
any string hereS##Eany string here.pdf
/ | ^-- "E" letter
"S" letter digit
extract the string that ends before the "E" after the "S-digit" delimiter and move the file to a folder with such a name, "S" and "E" letters are not case sensitive. Ignore files that have not the previous format."
This code solve the problem based on such specifications:
#echo off
setlocal EnableDelayedExpansion
rem Change current directory to the one where this .bat file is located
cd "%~P0"
set "digits=0123456789"
rem Process all *.pdf files
for %%f in (*.pdf) do (
rem Get the folder name of this file
call :getFolder "%%f"
rem If this file have a properly formatted name: "headS##Etail"
if defined folder (
rem Move the file to such folder
if not exist "!folder!" md "!folder!"
move "%%f" "!folder!"
)
)
goto :EOF
:getFolder file
set "folder="
set "file=%~1"
set "head="
set "tail=%file%"
:next
for /F "delims=%digits%" %%a in ("%tail%") do set "head=%head%%%a"
set "tail=!file:*%head%=!"
if not defined tail exit /B
if /I "%head:~-1%" equ "S" goto found
:digit
if "!digits:%tail:~0,1%=!" equ "%digits%" goto noDigit
set "head=%head%%tail:~0,1%"
set "tail=%tail:~1%"
goto digit
:noDigit
goto next
:found
for /F "delims=Ee" %%a in ("%tail%") do set "folder=%head%%%a"
exit /B
To use this Batch file, place it on the same folder where the original files are located and execute it without parameters; you may also execute it via a double-click in the explorer. Example session:
C:\Users\Antonio\Documents\test> dir /B
test.bat
The_Good_Wife_S06e15.pdf
The_Good_Wife_S06e22.pdf
TOCCO_ANGELO_4.pdf
True Blood S07e07_001.pdf
True Detective S02E03-04 Repack.pdf
True Detective S02e03.pdf
True Detective S02e03_001.pdf
True.Detective.S02e02.1080p.WEBMux.pdf
Tudors S04e08.pdf
Tutti pazzi per amore s3e15-16.pdf
Tutto Pu‗ Succedere S01e01-02.pdf
Twin Peaks s1e1-8.pdf
Twin Peaks s2e16-22.pdf
Tyrant S02e07.pdf
Tyrant.S01e01_02.720p.DLMux.pdf
Ultimo 2 - La Sfida.pdf
Ultimo 3 -L Infiltrato.pdf
Una Mamma Imperfetta S02e01-13.pdf
Under the Dome S02e02 Versione 720p.pdf
Under.the.Dome.S03E07.HDTV.x264.SUB.ITA.pdf
C:\Users\Antonio\Documents\test> test.bat
C:\Users\Antonio\Documents\test> tree /F
Listado de rutas de carpetas
El número de serie del volumen es 00000088 0895:160E
C:.
│ test.bat
│ TOCCO_ANGELO_4.pdf
│ Ultimo 2 - La Sfida.pdf
│ Ultimo 3 -L Infiltrato.pdf
│
├───The_Good_Wife_S06
│ The_Good_Wife_S06e15.pdf
│ The_Good_Wife_S06e22.pdf
│
├───True Blood S07
│ True Blood S07e07_001.pdf
│
├───True Detective S02
│ True Detective S02E03-04 Repack.pdf
│ True Detective S02e03.pdf
│ True Detective S02e03_001.pdf
│
├───True.Detective.S02
│ True.Detective.S02e02.1080p.WEBMux.pdf
│
├───Tudors S04
│ Tudors S04e08.pdf
│
├───Tutti pazzi per amore s3
│ Tutti pazzi per amore s3e15-16.pdf
│
├───Tutto Pu‗ Succedere S01
│ Tutto Pu‗ Succedere S01e01-02.pdf
│
├───Twin Peaks s1
│ Twin Peaks s1e1-8.pdf
│
├───Twin Peaks s2
│ Twin Peaks s2e16-22.pdf
│
├───Tyrant S02
│ Tyrant S02e07.pdf
│
├───Tyrant.S01
│ Tyrant.S01e01_02.720p.DLMux.pdf
│
├───Una Mamma Imperfetta S02
│ Una Mamma Imperfetta S02e01-13.pdf
│
├───Under the Dome S02
│ Under the Dome S02e02 Versione 720p.pdf
│
└───Under.the.Dome.S03
Under.the.Dome.S03E07.HDTV.x264.SUB.ITA.pdf
This code would be much simpler if the file name before the "S" delimiter can not have digits. This solution assumes that there are not exclamation-marks ! in the file names.
The easiest way to get the last instance of a regex substring is to break the string into chunks and process the chunks in reverse order.
:: A script for grouping PDF files based on book series name
:: http://i.imgur.com/seh6p.gif
#echo off
setlocal enabledelayedexpansion
cls
:: Main Directory Containing PDF Directories (change this to suit your needs)
set "source_dir=.\test"
:: Move to source dir and process each folder, one at a time.
pushd "%source_dir%"
for /f "delims=" %%A in ('dir /b /a:d') do (
call :getSeriesName "%%A" series_name
mkdir !series_name! 2>nul
REM If you want to do additional cleanup, change the copy to a move
copy "%%A\*.pdf" !series_name! >nul
)
popd
exit /b
::------------------------------------------------------------------------------
:: Extracts the series name from the directory and changes spaces to periods
::
:: Arguments: %1 - The original book release name
:: %2 - The variable that will contain the returned value because
:: batch doesn't actually have functions
:: Returns: The series name and volume number
::------------------------------------------------------------------------------
:getSeriesName
:: Convert spaces to periods
set "raw_name=%~1"
set standard_name=!raw_name: =.!
:: Split the folder name into period-delimited tokens
set token_counter=0
:name_split
for /f "tokens=1,* delims=.-" %%B in ("!standard_name!") do (
set name_part[!token_counter!]=%%B
set standard_name=%%C
set /a token_counter+=1
goto :name_split
)
:: Get the volume number
for /l %%B in (0,1,!token_counter!) do (
echo !name_part[%%B]!|findstr /R /C:"[sS][0-9][0-9]*[eE][0-9][0-9]*" >nul
if !errorlevel! equ 0 (
set /a name_end=%%B-1
set volume_value=!name_part[%%B]!
set volume_value=!volume_value:~0,3!
)
)
:: Rebuild the series name
set "extracted_name="
for /l %%B in (0,1,!name_end!) do set "extracted_name=!extracted_name!!name_part[%%B]!."
set extracted_name=!extracted_name!!volume_value!
:: Purge the name_part array
for /l %%B in (0,1,!token_counter!) do set "name_part[%%B]="
:: Return the extracted name
set "%~2=!extracted_name!"