I have file with thousand lines like this.
x:/folder/folder/PN.xxx
y:/folder/PN.xxx
...
I need to make script for change this (for using in excel):
x;file.xxx
y;file.yyy
Best result for me is something this:
x;PN;x:/folder/folder/
y;PN;y:/folder/
I try to use easiest way in cmd:
D:\>powershell -Command "(gc serverPN.txt) -replace 'xxx', ';' | Out-File myFile.txt"
But I dont know, what is needet to write in XXX, when i want remove everything between /*/. Something like this can be close:"\\[\D]*\\'".
Thanks for help.
Supposing the lines are stored in text_file.txt, for a batch-file solution you need a for /F loop and the ~ modifiers for loop variable expansion (see also for /? for details):
for /F "usebackq delims=" %%L in ("text_file.txt") do (
echo(%%~dL;%%~nxL;%%~dpL
)
This reaults in the following output with respect to the sample data in your question:
x:;PN.xxx;x:/folder/folder/
y:;PN.xxx;y:/folder/
If you want the pure file name without extension in the output, replace %%~nxL by %%~nL.
In PowerShell try:
gc "serverPN.txt" | % {
get-item $_ | %{
"$($_.PSDrive);$($_.Name);$($_.Directory)" | Out-File "myFile.txt" -Append
}
}
This should be your requested regular expression: :.*/ and you can replace it with a ;:
powershell -Command "(gc serverPN.txt) -replace ':.*/', ';' | Out-File myFile.txt"
$test = 'C:/test1/test2/test3/test4/testfile.xls'
$newfile =$test.Substring(0,2) + ';' + $test.Substring($test.LastIndexOf('/')+1,($test.Length-$test.LastIndexOf('/')-1))
Output :
C:;testfile.xls
While it is not the beloved PowerShell, cmd could also produce the "best result".
C:>TYPE fi2.bat
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "usebackq tokens=*" %%f IN (`TYPE fi2.txt`) DO (
SET DRIVE=%%~df
SET DRIVE=!DRIVE:~0,1!
SET FILENAME=%%~nf
SET DIRNAME=%%~pf
SET DIRNAME=!DIRNAME:\=/!
ECHO !DRIVE!;!FILENAME!;%%~df!DIRNAME!
)
EXIT /B 0
C:>CALL fi2.bat
x;PN;x:/folder/folder/
y;PN;y:/folder/
Related
I have multiple folders with these names:
ABC_03_00_016_0
ABC_03_00_016_1
ABC_03_00_016_2
ABC_03_00_016_3
ABC_03_00_016_4
ABC_03_00_016_5
What I want to do is to retain the folder with largest number in the end of folder name, i.e. ABC_03_00_016_5 in above case using PowerShell or batch commands.
How to get the folder with greatest number?
Maybe this could be done more elegant, but it's probably working as you want. I'm stripping the last digits, converting & comparing them to then determine the highest one. As you can see, order does not matter:
$items =
"ABC_03_00_016_0",
"ABC_03_00_016_100",
"ABC_03_00_016_99"
[int]$highestNumberTillNow = 0
$highestitem = ""
foreach ($item in $items){
[int]$number = $item.substring($item.LastIndexOf("_")+1,$item.length-$item.LastIndexOf("_")-1)
if ($number -gt $highestNumberTillNow){
$highestNumberTillNow = $number
$highestitem = $item
}
}
write-host $highestitem
You can use this:
#echo off
setlocal enabledelayedexpansion
pushd "%~dp0"
for /f "tokens=4 delims=_" %%a in ('dir ABC_03_016_* /ad /b') do (
for /f "tokens=* delims= " %%b in ('dir ABC_03_016_* /ad /b') do (
set dir[%%a]=%%b
)
)
set dc=0
:loop
set /a dc=dc+1
if defined dir[%dc%] goto loop
goto break
:break
set /a dc=dc-1
echo The folder is !dir[%dc%]!
pause >nul
Assuming you could have more folders with a similar name in the root path like
ABC_03_00_016_0
ABC_03_00_016_1
ABC_03_00_016_2
ABC_03_00_016_3
ABC_03_00_016_4
ABC_03_00_016_5
DEF_03_00_016_0
DEF_03_00_016_1
DEF_03_00_016_10
Using PowerShell you can use something like below.
This will return the folder object(s) with the highest number at the end of the name:
$lastFolder = Get-ChildItem -Path 'D:\test' -Directory | Where-Object { $_.Name -match '(.+)_(\d+)$' } |
Group-Object -Property #{Expression={ $matches[1] }} | ForEach-Object {
$_.Group | Sort-Object -Property #{Expression={ [int]$matches[2] }} | Select-Object -Last 1
}
# for demo, just output the FullName property of the folders found
$lastFolder.FullName
Output:
D:\test\ABC_03_00_016_5
D:\test\DEF_03_00_016_10
Regex details:
( Match the regular expression below and capture its match into backreference number 1
. Match any single character that is not a line break character
+ Between one 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 2
\d Match a single digit 0..9
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
$ Assert position at the end of the string (or before the line break at the end of the string, if any)
If you're wanting to remove all of the directories except for the one ending with the largest number, then I'd suggest PowerShell too:
Get-ChildItem -Path 'C:\Users\Naqqash\Desktop' -Filter '*_*_*_*_*' -Directory |
Where-Object { $_.Name -Match '(.+)_(\d+)$' } |
Sort-Object -Property { [Int]$($_.Name).Split('_')[-1] } |
Select-Object -SkipLast 1 |
Remove-Item
Please remember to adjust the path on line 1 to that holding your directories.
The example above requires PowerShell v5.0
The first method can be used only if all folder names have the same length, i.e. leading zeros are used to make sure that all numbers in all folder names have same number of digits.
#echo off
set "LastFolder="
for /F "eol=| delims=" %%I in ('dir "%~dp0ABC_03_00_016_*" /AD /B /O-N 2^>nul') do set "LastFolder=%%I" & goto HaveFolder
echo Found no folder matching pattern ABC_03_00_016_* in "%~dp0".
goto :EOF
:HaveFolder
echo Last folder according to sorted folder names is: %LastFolder%
The task to get folder name with greatest last number is more difficult on number of digits differs on last number.
#echo off
set "LastFolder="
setlocal EnableExtensions EnableDelayedExpansion
set "FolderNumber=-1"
for /F "eol=| delims=" %%I in ('dir "%~dp0ABC_03_00_016_*" /AD /B 2^>nul') do (
for /F "eol=| tokens=5 delims=_" %%J in ("%%I") do (
if %%J GTR !FolderNumber! (
set "LastFolder=%%I"
set "FolderNumber=%%J"
)
)
)
endlocal & set "LastFolder=%LastFolder%"
if not defined LastFolder (
echo Found no folder matching pattern ABC_03_00_016_* in "%~dp0".
) else (
echo Last folder according to last number in name is: %LastFolder%
)
Note: The last number in folder name should have never leading zeros on using the second code above. A number with one or more leading 0 is interpreted as octal number which means 08, 09, 18, 19, etc. are invalid octal numbers and are interpreted for that reason with value 0 by command IF on making the integer comparison. There would be additional code necessary above the IF condition to first remove all leading 0 from number string before doing the integer comparison.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains %~dp0 (drive and path of batch file ending with a backslash).
dir /?
echo /?
endlocal /?
for /?
if /?
set /?
setlocal /?
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
I need to add a progressive number of fixed length at the beginning of each row in a txt file. For example:
0001 aaaaaaaaaaa
0002 bbbbbbbbbb
...
0010 gggggggggg
I created a .bat file to run a PowerShell which should solve the problem:
#echo off &setlocal
set "path=C:\Users..."
set "filein=%~1"
set "fileout=%filein%_out"
setlocal EnableDelayedExpansion
call %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "& {(Get-Content %path%\%filein%.txt) |ForEach-Object {$_.Insert(0,($id++).PadLeft(10,'0'))} |Set-Content %path%\%fileout%.txt}"
But it doesn't work. Probably there's some syntax error.
($id++).PadLeft(10,'0') fails, because ($id++) is of type [int], not [string], and [int] has no .PadLeft() method.
Simply converting ($id++) to a string is enough:
($id++).ToString().PadLeft(10,'0')
Also note that your sample output has a space between the padded number and the content of the line, so you'd have to use:
$_.Insert(0, ($id++).ToString().PadLeft(10,'0') + ' ')
As an aside:
You don't need call in a batch file to call executables (call is only needed for calling other batch files, if you want the call to return).
The PowerShell executable is in the %PATH% by default, so you can invoke it by name only, i.e., powershell.exe.
since you added the [batch-file] tag - here is a pure Batch solution:
#echo off
setlocal enabledelayedexpansion
set count=0
(for /f "delims=" %%A in (input.txt) do (
set /a count+=1
set "index=00000!count!"
echo !index:~-4! %%A
))>Output.txt
Something like this might workout for you -
$id = 1
$files = Get-Content "\\PathToYourFile\YourFile.txt"
foreach ($file in $files)
{
$Padded = $id.ToString("0000000000");
$file.Insert(0, ($Padded));
$id++
}
Use the ToString() method, instead of PadLeft to add progressive number of fixed length zeroes. That is much simpler and hassle-free.
Also, doing the entire operation in PowerShell will be much simpler.
You can also do this in a single line like -
$i = 1
Get-Content "\\PathToYourFile\YourFile.txt" | % { $_.Insert(0, ($i.ToString("0000000000"))); $i++ } | Set-Content ".\NewFileout.txt"
Here is a pure batch file solution, which does not ignore empty lines and is safe against all characters, even exclamantion marks (!):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set /A "IDX=0"
> "output.txt" (
for /F "delims=" %%L in ('findstr /N "^" "input.txt"') do (
set /A "IDX+=0"
set "LINE=%%L"
setlocal EnableDelayedExpansion
set "IDX=0000!IDX!"
echo !IDX:~-4! !LINE:*:=!
endlocal
)
)
endlocal
exit /B
This PowerShell code selects the correct value for var5 from this string. The desired result it "Alert Raised".
PS C:\src\t> $s = 'Status 58 var5=Alert Raised on: March'
PS C:\src\t> $s
Status 58 var5=Alert Raised on: March
PS C:\src\t> $s | Where-Object { $_ -match '.*var5=(.*)\s+\w+:' } | ForEach-Object { $Matches[1] }
Alert Raised
However, using the same powershell code in a cmd shell script produces a different result. Why is that? Is there something that needs to be escaped for the cmd shell?
C:>type mat002-annex.bat
#ECHO OFF
SET "S=Status 58 var5=Alert Raised on: March"
ECHO S is set to %S
FOR /F %%a IN ('powershell -NoLogo -NoProfile -Command ^
" '%S%' | Where-Object { $_ -match '.*var5=(.*)\s+\w+:' } | ForEach-Object { $Matches[1] } "') DO (ECHO Result is "%%a")
C:>mat002-annex.bat
S is set to S
Result is "Alert"
PetSerAl, as he often does, has provided the crucial pointer in a comment:
cmd.exe's for /f loop by default breaks its input lines into tokens by whitespace, so that for /f %%a in (...) do only places the first such token in variable %%a.
This default behavior can be modified with an options string: for /f "<options>" %%a in (...) do.
Running for /? from a cmd.exe prompt describes its syntax:
eol=c - specifies an end of line comment character
(just one)
skip=n - specifies the number of lines to skip at the
beginning of the file.
delims=xxx - specifies a delimiter set. This replaces the
default delimiter set of space and tab.
tokens=x,y,m-n - specifies which tokens from each line are to
be passed to the for body for each iteration.
This will cause additional variable names to
be allocated. The m-n form is a range,
specifying the mth through the nth tokens. If
the last character in the tokens= string is an
asterisk, then an additional variable is
allocated and receives the remaining text on
the line after the last token parsed.
usebackq - specifies that the new semantics are in force,
where a back quoted string is executed as a
command and a single quoted string is a
literal string command and allows the use of
double quotes to quote file names in
file-set.
If you want each input line to be captured in a single variable, you have two options:
Note: The examples below use the following PowerShell command line, which simply outputs a string literal with multiple leading, inner, and trailing spaces, one two .
powershell -command " ' one two ' "
To make quoting easier, option usebackq is used below, which allows embedding the command in `...`.
Also note that it's good practice to add -NoProfile to powershell.exe's command line to prevent unnecessary - and potentially behavior-changing - loading of a user's profile; I've omitted it below for brevity.
"delims=": preserving the line exactly as-is
#echo off
for /f "usebackq delims=" %%a in (`powershell -command " ' one two ' "`) do echo Result is "%%a"
The above yields:
Result is " one two "
Note how the leading and trailing spaces were preserved too.
Important: for delims= to be recognized as deactivating delimiter-based (separator-based) parsing, it must be placed at the very end of the options string.
"tokens=*": trimming leading whitespace
#echo off
for /f "usebackq tokens=*" %%a in (`powershell -command " ' one two ' "`) do echo Result is "%%a"
The above yields:
Result is "one two "
Note how the leading spaces were trimmed.
It is curious that the trimming is only applied to leading whitespace, and there appears to be no direct way to trim trailing whitespace as well.
You don't need the for loop in the batch file.
#ECHO OFF
SET "S=Status 58 var5=Alert Raised on: March"
powershell -NoLogo -NoProfile -Command " '%S%' | Where-Object { $_ -match '.*var5=(.*)\s+\w+:' } | ForEach-Object { $Matches[1] } "
The above gives the expected result.
#echo off
setlocal EnableDelayedExpansion
for /f "delims=; tokens=1-5" %%i in ('type listing.csv') do (
echo %%k | find "CNSHA" > nul
if !errorlevel!==0 echo Searched_Value "CNSHA" was found in %%i: %%j:%%k
)
pause
With file listing.csv I would like to save the search result to another file listing2.txt.
In which lines of code to be include in the code formula >> listing2.txt to save the result of the sort or similar?
You can append to a file inside a loop like this:
for ... %%i in (...) do (
echo something >> output.txt
echo or other
)
The above will put only "something" lines in the output file while the "or other" lines are printed to the console.
You can also write the entire loop output to a file by putting the redirection operator outside the loop:
for ... %%i in (...) do (
echo something
echo or other
) >> output.txt
That will put both "something" and "or other" lines into the output file.
Note that when using the append redirection operator (>>) you need to truncate or remove an already existing file if you don't want to preserve content that was present prior to the loop:
type nul> output.txt
You can avoid this additional step by using the write redirection operator (>), but for that you need to put the whole loop in parentheses:
(for ... %%i in (...) do (
echo something
echo or other
)) > output.txt
BTW, you can also put redirection operators at the beginning of a line, and it's good practice to do so, because it avoids involuntarily adding trailing spaces to output lines:
for ... %%i in (...) do (
>>output.txt echo something
echo or other
)
or
>>output.txt for ... %%i in (...) do (
echo something
echo or other
)
Of course you can also mix the two approaches (redirecting inside and outside the loop), so you get some output in one file and the rest of the output in another:
>>output1.txt for ... %%i in (...) do (
>>output2.txt echo something
echo or other
)
If you want filtered records from the CSV as the output, you need to reconstruct the output lines, though:
>>output.csv echo %%~i;%%~j;%%~k
In your particular scenario it might be easier to just use find instead of working with a loop:
>output.csv (type input.csv | find ";CNSHA")
Since you also tagged your question powershell I'm going to throw in a PowerShell solution as well, just for good measure:
$headers = 'foo', 'bar', 'baz'
Import-Csv 'input.csv' -Delimiter ';' -Header $headers | Where-Object {
$_.baz -eq 'CNSHA'
} | Export-Csv 'output.csv' -NoType
The above writes all records where the 3rd field has the value "CNSHA" to a new file. Remove the -Header $headers if your input CSV comes with headers. Change the Export-Csv statement to something like this if you don't want headers in the output:
... | ConvertTo-Csv -NoType | Select-Object -Skip 1 | Set-Content 'output.csv'
Is there a reason why you cannot just use:
Find "CNSHA"<"listing.csv">"listing2.txt"
Presuming a possible previous file listing2.txt should be overwritten:
#echo off
setlocal
(for /f "delims=; tokens=1-5" %%i in (
'find /i "CNSHA" listing.csv'
) do echo Searched_Value "CNSHA" was found in %%i: %%j:%%k
) >listing2.txt
pause
Otherwse double the > the redirection to >>
I'm new to PowerShell, but I would like to use it, because there isn't a easy way to get the length of a string in Windows batch.
I need to write a piece of code in PowerShell that go through each line in a .txt file and determine the character length of that line. If the character length is over 250 then....etc.
The ....etc part is not important at the moment :)
In Windows batch I would write it like this:
FOR /F %%A IN ("C:\TestFile.txt") DO (
SET LINE=%%A
If LINE > 250 characters then ( ' This line is made up
....etc
)
How can I do it?
The following will do what you want:
$data = Get-Content "C:\TestFile.txt"
foreach($line in $data)
{
if ($line.Length -gt 250) {
Write-Host "A match"
}
}
Try this:
:: Not fully tested:
for /f "delims=" %%s in (C:\TestFile.txt) do (
set "x=%%s" & set /A y+=1
setlocal enabledelayedexpansion
for /f "skip=1 delims=:" %%i in ('"(set x&echo()|findstr /o ".*""') do set/a n=%%i-4
if !n! gtr 250 echo Line !y! Length !n!
endlocal
)
Was looking for this today and found an elegant solution here: https://softwarerecs.stackexchange.com/questions/38934/finding-the-longest-line-of-a-document/38936
GC "c:\folder\file.txt" | Measure -Property length -Maximum | Select Maximum
GC "c:\folder\file.txt" | Sort -Property length | Select -last 1
Important: credit goes to Pimp Juice IT from the link above, I'm just copy/pasting : )