Unable to escape pipe character (|) in powershell - windows

I am trying to find number of pipe(|) characters in each line of a file. I am using following command to do so.
gc .\test.txt | % { ($_ | select-string "|" -all).matches | measure | select count }
Its not counting the pipe symbol.
I also tried
`|
'|'
 |
Can anyone tell me how to escape pipe character in power shell?
If i am using strings or characters other than pipe the command is working properly.

A backslash \ is the escape character for a regular expression.
gc .\test.txt | % { ($_ | select-string "\|" -all).matches | measure | select count }
If you're unsure, you can always use [RegEx]::Escape():
$pattern = [RegEx]::Escape("|")
gc .\test.txt | % { ($_ | select-string $pattern -all).matches | measure | select count }
The pipe otherwise does not have to be escaped in PowerShell inside a string.

Use the -SimpleMatch parameter for Select-String, which turns off regular expression matching.

Related

PowerShell rename files

I have a database full of .pdf and .dwf files.
I need to rename these.
The files are named as follows:
123456 text text.pdf
And should look like this:
123456000_text_text.text.pdf
I can replace the spaces with the following command:
dir | rename-item -NewName {$_.name -replace " ","_"}
Now I need a command to insert "0" three times after the first 6 digits.
Can someone help me?
Thanks already
You need to filter on *.pdf and *.dwf files only and also if the filenames match the criterion of starting with 6 digits followed by a space character. Then you can use regex replacements like this:
Get-ChildItem -Path D:\Test -File | Where-Object { $_.Name -match '^\d{6} .*\.(dwf|pdf)$' } |
Rename-Item -NewName { $_.Name -replace '^(\d{6}) ', '${1}000_' -replace '\s+', '_'}
Before:
D:\TEST
123456 text text.dwf
123456 text text.pdf
123456 text text.txt
After:
D:\TEST
123456 text text.txt
123456000_text_text.dwf
123456000_text_text.pdf
Regex details of filename -match:
^ Assert position at the beginning of the string
\d Match a single digit 0..9
{6} Exactly 6 times
\ Match the character “ ” literally
. Match any single character that is not a line break character
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
\. Match the character “.” literally
( Match the regular expression below and capture its match into backreference number 1
Match either the regular expression below (attempting the next alternative only if this one fails)
dwf Match the characters “dwf” literally
| Or match regular expression number 2 below (the entire group fails if this one fails to match)
pdf Match the characters “pdf” literally
)
$ Assert position at the end of the string (or before the line break at the end of the string, if any)
What you have is 123456 text text.pdf
Want it to look like 123456000_text_text.pdf
A systematic way to achieve this would be>>
$const = "123456 text text.pdf"
$filename = $const -replace " ","_"
$temp = $filename.split("_")[0]
$rep1 = ([string]$temp).PadRight(9,'0')
$output = $filename -replace $temp,$rep1
Write-Host $output -ForegroundColor Green
The great thing about this method is that it will always trail with 0s keeping your number string to 9 digits.

Search for numbers in files and replace the match with "result -1"

I want to subtract 1 from every number before the character '=' in a list of files. For example, rename a string in a file such as "sometext.10.moretext" to "sometext.9.moretext".
I thought this might work:
grep -rl "[0-9]*" ./TEST | xargs sed -i "s/[0-9]*/&-1/g"
But it merely adds "-1" as a string after my numbers, so that the result is "sometext.10-1.moretext". I'm not really experienced with bash (and using it via windows), is there a way to do this? Powershell would also be an option.
edit
Input: some.text.10.text=some.other.text.10
Desired Output: some.text.9.text=some.other.text.10
Note: The actual number can be something from 1 to 9999.
File names have the following pattern: text#name#othername.config
You can use awk to achieve this :
I believe your words in strings are delimited by . and digits won't appear just before = otherwise this will fail as we are using only . as the delimiter.
awk 'BEGIN{FS=OFS="."} { for(i=1; i<=NF; i++) { if($i~"=")break; if($i~/^[0-9]+$/){$i=$i-1} }}1'
Input :
sometext.10.moretext=meow.10.meow
Output:
sometext.9.moretext=meow.10.meow
PowerShell
Get-ChildItem -Recurse | # List files
Select-String -Pattern '[0-9]' -List | # 'grep' files with numbers
Foreach-Object { # loop over those files
(Get-Content -LiteralPath $_.Path) | # read their content
ForEach-Object { # loop over the lines, do regex replace
[regex]::Replace($_, '[0-9]+', {param($match) ([int]$match.Value) - 1})
} | Set-Content -Path $_.Path -Encoding ASCII # output the lines
}
or in short form
gci -r|sls [0-9] -lis|% {(gc -lit $_.Path)|%{
[regex]::Replace($_,'[0-9]+',{"$args"-1})
}|sc $_.Path -enc ascii}
You can try
echo "sometext.10.moretext=meow.10.meow" |
sed -r 's/([^0-9]*)([0-9]*)(.*)/echo "\1$((\2-1))\3"/e'
Or changing files under TEST (see EDIT)
sed -ri 's/([^0-9]*)([0-9]*)(.*)/echo "\1$((\2-1))\3"/e' $(find ./TEST -type f)
EDIT
The find command will cause problems when filenames with spaces or newlines are encountered. So you should change the approach:
(Do not use grep -rlz "[0-9]*" ./TEST, that failed earlier)
find TEST -type f -print0 | xargs -0 sed -ri 's/([^0-9]*)([0-9]+)(.*)/echo "\1$((\2-1))\3"/e'
echo sometext.10.moretext=meow.10.meow| awk '{sub(/10/,"9")}1'
sometext.9.moretext=meow.10.meow

PowerShell replace the first three spaces with commas

I have a file that looks like this. There are many lines in this format.
5/10 RED SYSID This is a long message
I would like to have these line be in 4 comma-separated columns.
5/10,RED,SYSID,This is a long message
How can I replace only the first three spaces with commas?
You can do this with the PowerShell -split and -join operators.
$line -split ' ',3 -join ','
This example will convert the first three spaces into commas. -split ' ',3 will split the string into an array of four elements separated by the first three spaces in the string. Then -join ',' will rejoin them into one string with a comma between each.
The above won't work if your input has multiple spaces between fields since each space is considered separately, or if your fields are separated by other whitespace such as tabs. Instead, use a regex split.
$line -split '\s+',3,"RegexMatch" -join ','
This example treats as a delimiter the first three matches of \s+ and converts a sequence of consecutive whitespace into a single comma.
To run against every line in a file, use Get-Content and Foreach-Object
Get-Content $filename | foreach {
$_ -split '\s+',3,"RegexMatch" -join ','
} | Out-File $newfilename
The following regex should do what you want.
$line -replace '^(\S+?) (\S+?) (\S+?) (.*)','$1,$2,$3,$4'
This captures four groups of non-whitespace characters separated by spaces, with the last group containing the remainder of the string. Then it replaces them with those same four groups separated by commas.
To use this to modify every matching line in a file, Pipe Get-Content through Foreach-Object and finally to Out-File
$regex = [regex]'^(\S+?) (\S+?) (\S+?) (.*)','$1,$2,$3,$4'
Get-Content $filename | foreach {
$_ -replace $regex
} | Out-File $newfilename
Any lines the regex does not match will be sent to the output file unchanged. This includes if any lines contain tabs instead of spaces. If you need to test for this in your script, you can first test for $_ -match $regex, and take appropriate action if that returns false.
This might be what you're looking for.
Replace the first occurence of a string in a file
The relevant code is this:
$re = [regex]' '
$re.Replace([string]::Join("`n", (gc C:\Path\To\test.txt)), ',', 3)

How to assign command output to a variable

I want to assign the ouput of the following command to a variable in shell:
${arr2[0]} | rev | cut -c 9- | rev
For example:
mod=${arr2[0]} | rev | cut -c 9- | rev
echo $mod
The above method is not working: the output is blank.
I also tried:
mod=( "${arr2[0]}" | rev | cut -c 9- | rev )
But I get the error:
34: syntax error near unexpected token `|'
line 34: ` mod=( "${arr2[0]}" | rev | cut -c 9- | rev ) '
To add an explanation to your correct answer:
You had to combine your variable assignment with a command substitution (var=$(...)) to capture the (stdout) output of your command in a variable.
By contrast, your original command used just var=(...) - no $ before the ( - which is used to create arrays[1], with each token inside ( ... ) becoming its own array element - which was clearly not your intent.
As for why your original command broke:
The tokens inside (...) are subject to the usual shell expansions and therefore the usual quoting requirements.
Thus, in order to use $ and the so-called shell metacharacters (| & ; ( ) < > space tab) as literals in your array elements, you must quote them, e.g., by prepending \.
All these characters - except $, space, and tab - cause a syntax error when left unquoted, which is what happened in your case (you had unquoted | chars.)
[1] In bash, and also in ksh and zsh. The POSIX shell spec. doesn't support arrays at all, so this syntax will always break in POSIX-features-only shells.
mod=$(echo "${arr2[0]}" | rev | cut -c 9- | rev )
echo "****:"$mod
or
mod=`echo "${arr2[0]}" | rev | cut -c 9- | rev`
echo "****:"$mod

Can I use sed to manipulate a variable in bash?

In my program, I would like to first get the user input, and insert a \ before each /
so I write this, but it doesn't work.
echo "input a website"
read website
sed '/\//i\/' $website
Try this:
website=$(sed 's|/|\\/|g' <<< $website)
Bash actually supports this sort of replacement natively:
${parameter/pattern/string} — replace the first match of pattern with string.
${parameter//pattern/string} — replace all matches of pattern with string.
Therefore you can do:
website=${website////\\/}
Explanation:
website=${website // / / \\/}
^ ^ ^ ^
| | | |
| | | string, '\' needs to be backslashed
| | delimiter
| pattern
replace globally
echo $website | sed 's/\//\\\//g'
or, for better readability:
echo $website | sed 's|/|\\/|g'
You can also use Parameter-Expansion to replace sub-strings in variable.
For example:
website="https://stackoverflow.com/a/58899829/658497"
echo "${website//\//\\/}"
https:\/\/stackoverflow.com\/a\/58899829\/658497

Resources