In YAD, how do I set a field value processing the values of other fields? - yad

Example:
yad --form \
--field="A=":NUM "9" \
--field="B=":NUM "17" \
--field="A+B=":RO "?" \
--field="calc":FBTN "A=%1; B=%2; #echo 3:$((A+B))"
The A=%1 works. Calling another command instead of #echo 3: would work (for example using notify-send).
#echo 3:anyString works too, if it is the first sentence.
'#echo 3:$((%1+%2))' would set the value of the field "A+B=" to $((9+17)) instead of the result.
The following doesn't works too:
--field="calc":FBTN "#echo 3:$(adder %1 %2)"
(Assuming I have a function or command called adder)
Is there any other way to do this?
EDIT:
Strange for me:
--field="calc":FBTN "#echo 3:$(echo %1 + %2)"
uses the actual values of the fields 1 and 2: it works.
But any other command than echo does not work.
--field="calc":FBTN "#echo 3:$(expr %1 + %2)"
results in the message that "%1" isn't a number.
I'm using GNU bash, Version 5.0.17.

Related

How do I obtain regex matches of piped command using shell script?

First of all I'm trying to obtain a certain property from a KML file. For now, I tried
ogrinfo C:/test.kml -so -al | findstr "Extent"
which was recommended to me and outputs
Extent: (-100.054053, 33.702234) - (-94.647180, 37.125712)
I would require this in the form
-100.054053,-94.647180,33.702234,37.125712 for which I thought to use regex.
I tried the following just to see what it outputted:
ogrinfo C:/test.kml -so -al | findstr "Extent" | findstr /r /c:"-*[0-9]*\.[0-9]*"
but this still outputs
Extent: (-100.054053, 33.702234) - (-94.647180, 37.125712)
I read somewhere that Windows' FINDSTR only outputs the line where it matched and not the regex matches themselves. Is there some other way of doing it?
If I get that working I would save the matches in different variables somehow in a shell script. I'm no expert in shell scripting but I've been looking around and was thinking of doing something like this
#!/bin/bash
for /f "tokens=*" %%a in ('ogrinfo C:/test.kml -so -al ^| findstr "Extent" ^| findstr /r /c:"-*[0-9]*\.[0-9]*"') do (
echo %%a
#do something
)
done >output
but running this causes the shell to immediately disappears and can't even see the error.
Assumptions
You have a kml file with raw data.
You can extract a single line which starts with "Extent: " to get the values you want
Single line => there is only 1 line with that format in the kml file
The format of that line is:
Extent: (NUMBER1, NUMBER2) - (NUMBER3, NUMBER4)
A number can have the following characters: 0 1 2 3 4 5 6 7 8 9 . -
The output you want is:
NUMBER1,NUMBER3,NUMBER2,NUMBER4
Using Linux tools only, you can do this:
#!/bin/bash
#
datafile="data.kml"
# Ensure the data file exists
if [[ ! -f "$datafile" ]]
then
echo "ERROR: the data file does not exist."
exit 1
fi
# Extract the "Extent:" line
dataline=$(grep "Extent: " "$datafile")
# Make sure the line is of a valid format, and assign the number variables
if [[ $dataline =~ "Extent: ("([0-9.-]+)", "([0-9.-]+)") - ("([0-9.-]+)", "([0-9.-]+)")" ]] && number1="${BASH_REMATCH[1]}" && number2="${BASH_REMATCH[2]}" && number3="${BASH_REMATCH[3]}" && number4="${BASH_REMATCH[4]}"
then
echo "-----DEBUG-----"
echo "line==$dataline"
echo "1==$number1"
echo "2==$number2"
echo "3==$number3"
echo "4==$number4"
echo "-- END DEBUG --"
echo ""
echo "$number1,$number3,$number2,$number4"
else
echo "ERROR: there is no \"Extent: \" line in the data file ($datafile)"
fi
Details:
Everything is done in the if line.
=~ matches the left side with the pattern on the right side.
In the regular expression, you can define sections you want to reuse with ( ).
Ex: abcd(1)efgh(2)ijkl. The sections you can reuse are 1 and 2.
So in the if, each number is surrounded by parentheses.
When the =~ is processed, the BASH_REMATCH array is defined with each section.
The "DEBUG" echo statements can be removed or commented out.
If you have more than one "Extent: ..." in the KML file, you can loop on the lines and process each one at a time.

If variable can't have spaces?

I am making an experimental program in batch for a simple chatting interface. In this one, I made a function where if there is the word r placed in chat, it ignores it and just redisplays the text file again. It works fine if I put r and it just refreshes, and if I put one word it works fine, but if I put a word and a space and another word, it breaks and shows the following error:
Chat(Put r for refresh):hey hi
hi was unexpected at this time.
Does anyone know how to fix this? Thanks.
Code:
#echo off
cls
cd %USERPROFILE%\Desktop\Chat
for /f "delims=" %%A in (chat.txt) do (
set %%A
)
echo %chatt%
echo %chatp%
echo %chatn%
cd %USERPROFILE%\Desktop\Chat\Servers\%chatt%
:1
cls
type %chatn%.chat
set /p in=Chat(Put r for refresh):
if %in% == r goto 1
echo %chatp%: %in%>>%chatn%.chat
goto 1
The usual way to deal with spaces in a string variables contents is to wrap it in quotes. This is the case here. When you use the variables contents with %in% the contents are inserted verbatim, so the suspect line would look like this:
if hey hi == r goto 1
It starts off okay if hey but then instead of seeing a comparison operator like == it sees hi and chokes. So wrap it all in quotes:
if "%in%" == "r" goto 1
That way it will be interpreted like
if "hey hi" == "r" goto 1
and the bat engine will know that "hey hi" should be treated as one entity.

Batch file to read a txt with special characters and replace a word in it

I'm trying to make a batch file that reads a txt file "ayylmao.txt" and find a specific word "hello" and replaces it with "xello".
The thing is that the "ayylmao.txt" contains specific characters.
Ayylmao.txt looks something like this:
‹‹R‹Ę‹/M‹;Ču‹č˙˙˙‹‹#‰‹‹#CëC;Đu‹čq˙˙˙‹‹#C‹D$‰;7u®‹Ó‹Ćčúţ˙˙„Ŕu3Ŕ‰YZ]_^[ĂŤ# SVWUÄđ‰$‹ô‹‰D$‹
‹‹#;Č‚† ‹Ř‹>_‹ůz;ßrv;Ču!‹B‹A‹B‹)B‹x uV‹čđţ˙˙ëM‹Ř‹>_‹ůz;ßu
‹B‹)Bë3‹Z‰\$‹>‹‹.}+ű‰|$+Č‹‰HŤT$‹čMţ˙˙„Ŕu3 hello Ŕë°ë‹‹ ‰‹;D$…Y˙˙˙3ŔÄ]_^[ĂSVW‹Ú‹đţ }ľ ëĆ˙˙ ć ˙˙‰sjh Vj
You can see the "hello" word in the last line. I want the batch to go to the process and give me a ayylmao1.txt that looks like this:
‹‹R‹Ę‹/M‹;Ču‹č˙˙˙‹‹#‰‹‹#CëC;Đu‹čq˙˙˙‹‹#C‹D$‰;7u®‹Ó‹Ćčúţ˙˙„Ŕu3Ŕ‰YZ]_^[ĂŤ# SVWUÄđ‰$‹ô‹‰D$‹
‹‹#;Č‚† ‹Ř‹>_‹ůz;ßrv;Ču!‹B‹A‹B‹)B‹x uV‹čđţ˙˙ëM‹Ř‹>_‹ůz;ßu
‹B‹)Bë3‹Z‰\$‹>‹‹.}+ű‰|$+Č‹‰HŤT$‹čMţ˙˙„Ŕu3 xello Ŕë°ë‹‹ ‰‹;D$…Y˙˙˙3ŔÄ]_^[ĂSVW‹Ú‹đţ }ľ ëĆ˙˙ ć ˙˙‰sjh Vj
You can see that "hello" is now "xello".
I found this batch file that replaces a word from a text file:
#echo off
REM -- Prepare the Command Processor --
SETLOCAL ENABLEEXTENSIONS
SETLOCAL DISABLEDELAYEDEXPANSION
if "%~1"=="" findstr "^::" "%~f0"&GOTO:EOF
for /f "tokens=1,* delims=]" %%A in ('"type %3|find /n /v """') do (
set "line=%%B"
if defined line (
call set "line=echo.%%line:%~1=%~2%%"
for /f "delims=" %%X in ('"echo."%%line%%""') do %%~X
) ELSE echo.
)
This code works for files that don't have specific characters very good if use it like this:
code.bat "hello" "xello" "ayylmao.txt">"ayylmao1.txt"
This code only types in ayylmao1.txt few special characters but replaces hello. I want all the special characters typed in there.
I made it like this:
chcp 1252
code.bat "hello" "xello" "ayylmao.txt">"ayylmao1.txt"
But it didn't work. It worked just like the first code.
If there is a way in PowerShell to do this I'd be glad to hear it.
What you have there looks like a binary file, not a text file, despite the extension. Batch is no good for editing binary files. In PowerShell it's doable, but you need to resort to working with the data bytes instead of simple text.
This is a basic example that will find the first occurrence of the string "hello" in your file and replace it with "xhello":
$f = 'C:\path\to\ayylmao.txt'
$stext = 'hello'
$rtext = [char[]]'xhello'
$len = $stext.Length
$offset = $len - 1
$data = [IO.File]::ReadAllBytes($f)
# find first occurrence of $stext in byte array
for ($i=0; $i -lt $data.Count - $offset; $i++) {
$slice = $data[$i..($i+$offset)]
if (-join [char[]]$slice -eq $stext) { break }
}
# Once you know the beginning ($i) and length ($len) of the array slice
# containing $stext you can "cut up" $data and concatenate the slices before
# and after $stext to the byte sequence you want to insert ($rtext):
#
# |<-- $stext -->|
# [...]['h','e','l','l','o'][...] <-- $data
# ^ ^ ^ ^
# | | | |
# | $i | $i+$len
# $i-1 $i+$offset (== $i+$len-1)
#
$rdata = $data[0..($i-1)] + [byte[]]$rtext + $data[($i+$len)..($data.Count-1)]
[IO.File]::WriteAllBytes($f, $rdata)
You'll need to adjust this code if you want the replacement to work differently (replace other occurrences as well, replace a different occurrence, …).
But it didn't work. It worked just like the first code. Help ?
This batch code is coming from this site and there is a link to discussion why it doesn't work with special characters.
Yes, the PowerShell replace command can replace the string and keep the special characters. To call it from within your batch script, use the following line
powershell -command "(get-content Ayylmao.txt) -replace 'hello','xello' | set-content Ayylmao.txt"
If you want to enter your parameters from the command line, then the line would be
powershell -command "(get-content %3) -replace '%1','%2' | set-content %4"
And if you want to use variables defined in the batch script, it is the same as you would for any batch script
set file=Ayylmao.txt
set Search_criteria=hello
set Replace_criteria=xello
powershell -command "(get-content %file%) -replace '%Search_criteria%','%Replace_criteria%' | set-content %file%"

How to remove comments from text file

My text file contains one line comments that all being with "// ". Two forward slashes and a space. These may either take up the whole line or just the last part of a line. Each comment does not extend beyond the line that it's on. So no /* */ type comments crossing multiple lines.
In simple terms, all comments start with "//space" anywhere on the line. Anything starting with "//space" should be removed and trailing spaces on that line should also be removed. Leading spaces should stay. Any blank lines should be removed.
Sample file:
// This is a comment
x = 1 // This is also a comment after the double slash
x = 2
x = 3 // The above is a blank line
// Comment on this record but nothing precedes it, so should be deleted.
y = 4 // A line with leading spaces that should be kept.
z = "//path"; // The first double slashes are not a comment since the space is missing after the "//"
// Last comment line.
Result file (no trailing spaces, but keep leading spaces.:
x = 1
x = 2
x = 3
y = 4
z = "//path";
I can remove the blank lines using gc file.txt | Where-Object { $_ -ne ''} > result.txt. However I'm having trouble with reading just the beginning part of a line up to the "//" comment part.
I also tried findstr but haven't found how to read each line up to the "//" and then trim spaces out.
I could write a script program to loop throught the file and do this, but it seems like there should be a way to accomplish it using a simple one or two line powershell or bat file command.
What is the easiest way (shortest amount of code) to remove these comments while keeping the uncommented contents of the file?
Since you seem to equate "easy" with "short", here's a fairly simple solution:
gc .\samplefile.txt|%{$_-replace"(.*)(// .*)",'$1'}|?{$_}
if it's really that important to you :-)
A bit more verbose version (still using regex):
Get-Content .\samplefile.txt | Where-Object {
-not ([String]::IsNullOrEmpty($_.Trim()) -or $_-match"^\s*// ")
} |ForEach-Object { $_ -replace "(.*)(// .*)",'$1' }
That being said, I would (personally) go for a more verbose and easier-to-read/maintain solution:
To remove everything after //, the easiest way is to find the first occurrence of // with String.IndexOf() and then grab the first part with String.Substring():
PS C:\> $CommentedString = "Content // this is a comment"
PS C:\> $CommentIndex = $CommentedString.IndexOf('// ')
PS C:\> $CommentedString.Substring(0,$CommentIndex)
Content
For the indented comments you can also use String.Trim() to remove whitespace from the beginning and end of the string:
PS C:\> " // Indented comment" -match '^//'
True
You can use the ForEach-Object cmdlet to go through every line and apply the above:
function Remove-Comments {
param(
[string]$Path,
[string]$OutFile
)
# Read file, remove comments and blank lines
$CleanLines = Get-Content $Path |ForEach-Object {
$Line = $_
# Trim() removes whitespace from both ends of string
$TrimmedLine = $Line.Trim()
# Check if what's left is either nothing or a comment
if([string]::IsNullOrEmpty($TrimmedLine) -or $TrimmedLine -match "^// ") {
# if so, return nothing (inside foreach-object "return" acts like "coninue")
return
}
# See if non-empty line contains comment
$CommentIndex = $Line.IndexOf("// ")
if($CommentIndex -ge 0) {
# if so, remove the comment
$Line = $Line.Substring(0,$CommentIndex)
}
# return $Line to $CleanLines
return $Line
}
if($OutFile -and (Test-Path $OutFile)){
[System.IO.File]::WriteAllLines($OutFile, $CleanLines)
} else {
# No OutFile was specified, write lines to pipeline
Write-Output $CleanLines
}
}
Applied to your sample:
PS C:\> Remove-Comments D:\samplefile.txt
x = 1
x = 2
x = 3
Like a great many text processing problems, there is a simple solution using JREPL.BAT - a powerful regex text processing utility for the Windows command line. It is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. Full documentation is embedded within the script.
jrepl "^(.*?)\s*// " "$1!=''?$1:false" /jmatch /f test.txt /o out.txt
You can overwrite the original file by specifying - as the output file:
jrepl "^(.*?)\s*// " "$1!=''?$1:false" /jmatch /f test.txt /o -
I've tested, and it gives the exact output you are looking for.
If you put the command within a batch script, then you must use call jrepl
Tha Batch file below do what you want. Sorry, but there is not an "easy short code" way to do this...
#echo off
setlocal EnableDelayedExpansion
rem Set the maximum number of trailing spaces as a power_of_2-1 value. For example, for 15 spaces:
set spcPow2=4
set "spaces= "
for /L %%i in (1,1,%spcPow2%) do set "spaces=!spaces!!spaces!"
set /A spcPow2-=1
rem Process all lines, excepting empty ones and lines that start with "/"
setlocal DisableDelayedExpansion
for /F "eol=/ delims=" %%a in (test.txt) do (
set "line=%%a"
rem Split line at "// " and get the first part
setlocal EnableDelayedExpansion
for /F "delims=¡" %%b in ("!line:// =¡!") do (
endlocal
set "line=%%b"
)
rem Eliminate trailing spaces
setlocal EnableDelayedExpansion
set spc=0
for /L %%b in (%spcPow2%,-1,0) do (
set /A "newSpc=spc+(1<<%%b)"
for %%n in (!newSpc!) do if "!line:~-%%n!" equ "!spaces:~-%%n!" set "spc=%%n"
)
if !spc! gtr 0 for %%n in (!spc!) do set "line=!line:~0,-%%n!"
rem Show resulting line
if defined line echo !line!
endlocal
)
EDIT: New solution added
#set #x=1 // & CScript //nologo //E:JScript "%~F0" < samplefile.txt & goto :EOF
WScript.Stdout.Write(WScript.Stdin.ReadAll().replace(/(.*)\/\/ .*/g,"$1"))
Copy previous code into a file with .BAT extension, that is, it is a Batch file!

Passing parameters to powershell script

I'm trying to run a powershell script from the run dialog (will be used as a scheduled task), and I'm having troubles passing parameters.
The script will take in two parameters, named title and msg.
The script is located in: D:\Tasks Scripts\Powershell\script.ps1
This is what I'm trying to do:
powershell.exe -noexit 'D:\Tasks Scripts\Powershell\script.ps1' -title 'Hello world' -msg 'This is a test message'
But it fails upon reading the parameters.
Running .\script.ps1 -title 'Hello world' -msg 'This is a test message' on powershell works fine.
Use -file before the path to your script:
powershell.exe -noexit -file 'D:\Tasks Scripts\Powershell\script.ps1' etc...
I usually run powershell scripts from cmd.exe because this is portable
(works out-of-the-box on others' computers, like developer folks or clients):
no need to worry about Set-ExecutionPolicy or associating the .ps1 extension.
I create the file with .cmd extension (instead of .ps1), and copy&paste a short,
constant code to the first line(s) that invokes powershell.exe and passes the rest
of the file to it.
Passing arguments is tricky. I have multiple variants of the constant code
because the general case is painful.
when not passing arguments, the .cmd file looks like this:
#powershell -c ".(iex('{#'+(gc '%~f0' -raw)+'}'))" & goto :eof
# ...arbitrary PS code here...
write-host hello, world!
This uses the -Command argument of powershell.exe. Powershell reads the .cmd
file as text, puts it in a ScriptBlock with the first line commented out,
and evaluates it with the '.' command.
Further command line arguments
can be added to the Powershell invocation as required (e.g. -ExecutionPolicy Unrestricted,
-Sta etc.)
when passing arguments that do not contain spaces or are 'single-quoted'
(which is non-standard in cmd.exe), the one-liner is this:
#powershell -c ".(iex('{#'+(gc($argv0='%~f0') -raw)+'}'))" %* & goto :eof
write-host this is $argv0 arguments: "[$($args -join '] [')]"
param() declarations could be used as well, $args is not obligatory.
$argv0 is used to compensate for the missing $MyInvocation.PS* info.
Examples:
G:\>lala.cmd
this is G:\lala.cmd arguments: []
G:\>lala.cmd "1 2" "3 4"
this is G:\lala.cmd arguments: [1] [2] [3] [4]
G:\>lala.cmd '1 2' '3 4'
this is G:\lala.cmd arguments: [1 2] [3 4]
when passing arguments that are "double-quoted" but do not contain
the & and ' characters, I use a two-liner to replace all " with '
#echo off& set A= %*& set B=#powershell -c "$argv0='%~f0';.(iex('{'
%B%+(gc $argv0|select -skip 2|out-string)+'}'))" %A:"='%&goto :eof
write-host this is $argv0 arguments: "[$($args -join '] [')]"
(Note that the space is important in the A= %* assignment for the
argument-less case.)
Results:
G:\>lala.cmd
this is G:\lala.cmd arguments: []
G:\>lala.cmd "1 2" "3 4"
this is G:\lala.cmd arguments: [1 2] [3 4]
G:\>lala.cmd '1 2' '3 4'
this is G:\lala.cmd arguments: [1 2] [3 4]
the most general case passes the arguments via environment variables
thus Powershell's param() declaration does not work. In this case the
arguments are expected to be "double-quoted" and may contain ' or &
(except for the path of the .cmd file itself):
;#echo off & setlocal & set A=1& set ARGV0=%~f0
;:loop
;set /A A+=1& set ARG%A%=%1& shift& if defined ARG%A% goto :loop
;powershell -c ".(iex('{',(gc '%ARGV0%'|?{$_ -notlike ';*'}),'}'|out-string))"
;endlocal & goto :eof
for ($i,$arg=1,#(); test-path -li "env:ARG$i"; $i+=1) { $arg += iex("(`${env:ARG$i}).Trim('`"')") }
write-host this is $env:argv0 arguments: "[$($arg -join '] [')]"
write-host arg[5] is ($arg[5]|%{if($_){$_}else{'$null'}})
(Note that in the first line A=1& must not contain space.)
Result:
G:\>lala.cmd "a b" "c d" "e&f" 'g' "h^j"
this is G:\lala.cmd arguments: [a b] [c d] [e&f] ['g'] [h^j]
arg[5] is $null

Resources