I am currently learning LPTHW Ex 46. In his video tutorial, Zed had done the following commands:
Find NAME within files using grep -r "NAME" *.
Find all files with extension ending in .pyc using find . -name "*pyc" -print.
Unfortunately, the above code does not work on Windows PowerShell. May I know what their Windows PowerShell equivalents are?
Based on my search, item 1 can be replaced by Select-String. However, it is not as good as we can only search specific files and not directories. For example, while this would work:
Select-String C:\Users\KMF\Exercises\Projects\gesso\gesso\acrylic.py -pattern "NAME"
this would not:
Select-String C:\Users\KMF\Exercises\Projects\gesso -Pattern "NAME"
and it gives the following error
Select-String : The file C:\Users\KMF\Exercises\Projects\gesso can not be read: Access to the path 'C:\Users\KMF\Exercises\Projects\gesso' is denied.
For item 2 I could not find a similar function.
grep and find are Unix/Linux shell commands. They won't work in PowerShell unless you install a Windows port of them.
As you already found out, Select-String is the PowerShell equivalent for grep. It doesn't recurse by itself, though, so you have to combine it with Get-ChildItem to emulate grep -r:
Get-ChildItem -Recurse | Select-String -Pattern 'NAME'
For emulating find you'd combine Get-ChildItem with a Where-Object filter:
Get-ChildItem -Recurse | Where-Object { $_.Extension -eq '.pyc' }
PowerShell cmdlets can be aliased to help administrators avoid extensive typing (since PowerShell statements tend to be rather verbose). There are several built-in aliases, e.g. ls or dir for Get-ChildItem, and ? or where for Where-Object. You can also define aliases of your own, e.g. New-Alias -Name grep -Value Select-String. Parameter names can be shortened as long as the truncated parameter name remains unique for the cmdlet. When cmdlets allow positional parameters they can even be omitted entirely.
With all of the above your two PowerShell statements can be reduced to the following:
ls -r | grep 'NAME'
ls -r | ? { $_.Extension -eq '.pyc' }
Note however, that aliases and abbreviations are mainly intended as an enhancement for console use. For PowerShell scripts you should always use the full form, not only for readability, but also because aliases may differ from environment to environment. You don't want your scripts to break just because they're run by someone else.
Related
how do i store the output of a command into a variable for use in a github-actions .yaml?
docker images --format='{{.ID}}' | select -first 1
gives me:
fc6e040841a1
i've seen stuff online about select-object..but i honestly have no idea, just trying to push an image to a registry...
the following cmd doesn't work in powershell:
for /f "delims=" %a in ("docker images --format='{{.ID}}' | select -first 1") do #set "%_img%=%a"
the following cmd doesn't work in powershell:
for /f "delims=" %a in ("docker images --format='{{.ID}}' | select -first 1") do #set "%_img%=%a"
That's because this is Command Prompt syntax. Specifically, everything outside of the () would only work under cmd.exe. The PowerShell equivalent for assigning a command result to a variable is:
$variableName = COMMAND
To apply it to your use case:
$imageId = docker images --format='{{.ID}}' | Select-Object -First 1
Note that select is an alias of Select-Object and either can be used interchangeably.
Edit: While not required for setting variables unlike in the Command Prompt, for syntax is still different in PowerShell when batch scripting. You can read up on PowerShell's for, foreach, and ForEach-Object constructs when you want to learn how they are used in PowerShell scripts, and watch for this gotcha when using the foreach "statement" as part of a pipeline.
While not part of the original scope of the question, since OP did ask and I answered in the comments, I will put the bash equivalent here for the sake of completeness and how I transposed this from the PowerShell method I used above:
imageId=$(docker images --format="{{.ID}}" | head -n 1)
This is similar to the PowerShell syntax with a few changes: remove the $ from the variable name on assignment, and Select-Object is replaced by head. You can't pad the = with whitespace, and you have to subshell the command with $().
I am trying to search inside a folder containing several files. The name of the files is written in upper case with a .sub extension in lower case:
AAA.sub
BBB.sub
CCC.sub
DDD.sub
I am searching a pattern trough those file using grep, however i would like to only use lower case letter for the input files.
In the man page for grep it is written:
-i, --ignore-case
Ignore case distinctions in both the PATTERN and the input files. (-i is specified by POSIX.)
So, if i understood properly:
grep -i subckt /schematics/aaa
and
grep -i subckt /schematics/AAA
Are supposed to both be able to search a pattern "subckt" in the file "aaa" regardless of its case (AAA or aaa) and if two files named aaa and AAA are present at the same time in the foler, i expect grep to search trough both of them.
However when i try my search with the 1st instruction (lower case) it does not work, giving me "no such file or directory" message.
When i try to search with the 2nd instruction (upper case) it works properly.
I obviously understood something wrong about how the -i option with grep, can anyone give me an answer regarding this matter?
Is it possible to be case insensitive with the input files when using grep?
EDIT:
My question was lacking details, even tough i have found the answer to my problem i will add the details in case someone else stumbles upon this:
I have one file that contains a list of each file name i want to grep. My list looks like this:
aaa capacitor C_0
bbb capacitor C_0
ccc resistor R_in
...
The grep is done inside a perl script, the perl script parses the list file and gets the name of each individual file name (aaa bbb ccc) inside a while loop.
However the name inside the list file is written in lower case whereas the name of the files i want to grep is written in upper case.
This is why i wanted to have the input file search to be case insensitive so that i could directly do a grep -i subck aaa and it would search inside the file 'AAA'
However, since the grep is launched from a perl script, and since it is apparently not possible to have grep behave like that, i used the uc() function of perl to convert aaa to AAA and do my grep with it. (see my answer below)
-i affects how the contents are searched, not the name of the files.
When the man page says "Ignore case distinctions in both the PATTERN and the input files." that really means that case is ignored in the pattern ( searching for AAA and aaa are equivalent) and the contents of the input files (a line would match if it includes "AAA" or "aaa" or even "AaA")
I think you want to either list all the filenames on the command line, or find a glob (i.e. wildcard) that matches all the filenames:
grep -i subckt *.sub
In Unix/Linux shells (bash, zsh, and so on) "*" is processed by the shell (bash) not the command (grep). The command receives the list of files and actually can't tell the difference between whether a user typed "grep foo *" and "grep foo file1 file2 file3" (if the directory includes those 3 files)
Please try the following command
find . -iname aaa.sub | grep -rn subckt
find with -iname option will list out files ignoring their case. In the above case find . -iname will list out both aaa.sub & AAA.sub. The output is piped to the grep command.
I have found a way to circumvent my problem by using the uc (upper case) function of perl to convert the input files for the grep function into upper case.
The grep command was launched from a perl script in the first place:
grep -i subckt /schematics/aaa
So, i just did that in my perl script:
$tmp=aaa
$tmp=uc($tmp)
grep -i subckt /schematics/$tmp
Now, the "aaa" name is just an example. In the perl script it is recovered from another parsed file that is written in lower case.
Thanks for the answers tough.
grep uses the filenames as they are listed on the command line. The -i option affects the contents of the files, not the names of the files.
You can use find to select filenames to be searched. The -iname option lets you match files ignoring case.
grep subckt $(find /schematics -iname aaa.sub -print)
If you have many filenames, or those filenames include spaces or other characters that would confuse the shell, the safe and secure way to do this is using the -print0 and -0 options:
find /schematics -iname aaa.sub -print0 | xargs -r -0 grep -i subckt
I have a directory named XYZ which has directories ABC, DEF, GHI inside it. I want to search for a pattern 'writeText' in all *.c in all directories (i.e XYZ, XYZ/ABC, XYZ/DEF and XYZ/GHI)
What grep command can I use?
Also if I want to search only in XYZ, XYZ/ABC, XYZ/GHI and not XYZ/DEF, what grep command can I use?
Thank you!
grep -R --include="*.c" --exclude-dir={DEF} writeFile /path/to/XYZ
-R means recursive, so it will go into subdirectories of the directory you're grepping through
--include="*.c" means "look for files ending in .c"
--exclude-dir={DEF} means "exclude directories named DEF. If you want to exclude multiple directories, do this: --exclude-dir={DEF,GBA,XYZ}
writeFile is the pattern you're grepping for
/path/to/XYZ is the path to the directory you want to grep through.
Note that these flags apply to GNU grep, might be different if you're using BSD/SysV/AIX grep. If you're using Linux/GNU grep utils you should be fine.
You can use the following command to answer at least the first part of your question.
find . -name *.c | xargs grep "writeText"
I need to find ALL files that have multiple keywords anywhere in the file (not necessarily on the same line), given a starting directory like ~/. Does "grep -ro" do this?
(I'm using Unix, Mac OSX 10.4)
You can use the -l option to get a list of filenames with matches, so it's just a matter of finding all of the files that have the first keyword and then filtering that list down to the files that also have the second keyword:
grep -rl first_keyword basedir | xargs grep -l second_keyword
To search just *.txt
find ~/. -name "*.txt" | xargs grep -l first_keyword | xargs grep -l second_keyword
Thanks Adam!
I'm not so hot with PowerShell yet, but have managed to get this command to work quite nicely:
get-childitem "C:\Code\WC1" -Recurse | select-string "insert into\s+my_table"
Thing is, I know I'm going to struggle to remember this, so how can I make it into a function where the path supplied to get-childitem and the search regex are parameters?
I'm using PowerShell 2.0.
more commonly these days the parameters are being called after the function declaration e.g.
Function Find-Code {
param([string] $path, [string] $pattern)
get-childitem $path -Recurse | select-string $pattern
}
Function Find-Code([string] $path, [string] $pattern)
{
get-childitem $path -Recurse | select-string $pattern
}
You can put this in your PowerShell Profile.
An easy way to do this is to edit the $profile file (run something like notepad $profile from your PowerShell prompt) and just paste the text right in.