0

I'm aware of similar questions of this topic, but none of them actually answered the question the way I wanted.

My code:

$answer = 1
DO {
 Write-host "What u want?"
 Write-host "1.Add note"
 Write-host "2.Read notes"
 Write-host "3.Exit"
 $answer = Read-host Write a number between 1 to 3 and press Enter.

     if ($answer -eq 1) 
     {
          DO {  
            $msg = Read-host Write the note and press Enter:
            $path = Get-Location 
            Add-content $path\"DONTREMOVEME.txt" "- $msg"
            $res = Read-host "Added, want to add one more? y/n"
          } 
          while ($res -ne "n")

    Write-host "...back to the main menu"
    Start-Sleep -s 2
    }

    if ($answer -eq 2) 
    {
        $path = Get-Location 
        Get-content $path\"DONTREMOVEME.txt" 
        Read-host "Press Enter to go back to the main menu"
    }

} While ($respuesta -ne 3) 

Write-host Bye.

I really need to add a "delete notes" option but I have no clue and tried with sed, grep, get-content replace etc.

I need an option in the table that asks the user for a note (line) that wants removed (not in blanck, removed) after showing the list available (with cat, maybe?).

Something like:

if ($answer -eq 3) #let's say 'Exit' is now 4th option
{
    $path = Get-Location 
    Get-content $path\"DONTREMOVEME.txt" # or cat $path\"DONTREMOVEME.txt"
    $delete = Read-host which note you want removed? Type the number and press Enter
    #and actually deleting it.
}

Thank you so much in advance.

* EDIT: I'm so sorry, maybe I explained myself very poorly.

I don't want the code to check a matching string given by the user (that's why the previous questions and answers of stack overflow didn't actually work for me).

The notes inside the DONTREMOVEME.txt could be e.g.:

- Buy salt
- Buy bags
- Remember to feed cats
- Go for a walk

And the code has to display that and ask the user which line wants deleted (because the activity it's already done) as an int.

So user presses "3" and Enter, so "Remember to feed cats" disappears like this:

- Buy salt
- Buy bags
- Go for a walk

Searching for a matching string does not work because the variety and extension of the notes can make difficult to pinpoint 1 word and being sure it doesn't appear in another line...

Also, can't use a $count++ to every line so the user can use to match strings like this:

1 Buy salt
2 Buy bags
3 Remember to feed cats
4 Go for a walk

...because in the notes can be written "buy 2 bags".

I hope I made myself clear this time, I'm so sorry for the caused trouble.

Machavity
  • 30,841
  • 27
  • 92
  • 100
  • What do you plan to use as input for removing the line on your file? Easier way I can imagine right now is adding a "line number" before each line so the input can be an integer. – Santiago Squarzon Apr 06 '21 at 01:23
  • Another option and a pretty good one is using `Out-GridView` with the `-PassThru` switch. – Santiago Squarzon Apr 06 '21 at 01:26
  • @SantiagoSquarzon, but `Our-GridView` only works on Microsoft Windows for now. – lit Apr 06 '21 at 02:00
  • 1
    @lit, as for: `Out-GridView only works on Microsoft Windows`. ***This is no longer true for almost 3 years now. PowerShell Core (PSv7) has a console version of `Out-GridView`. Microsoft has talked about this and demo'd it at MSIgnite 2020 and 2021. Here is the 2019 announcement of it: [Out-GridView Returns!](https://devblogs.microsoft.com/powershell/out-gridview-returns/) `PowerShell Core debuted for Linux and Mac... Today, we are excited to announce that Out-GridView is debuting on all Core-supported platforms through the GraphicalTools Module.` – postanote Apr 06 '21 at 02:53
  • This is a very common use case and well documented, with many articles/examples/videos all over the web on the topic. What did you search for? [powershell 'replace string in line in a text file'](https://duckduckgo.com/?q=powershell+%27replace+string+in+line+in+a+text+file%27&t=h_&ia=web) – postanote Apr 06 '21 at 04:39

2 Answers2

0

Continuing from my comment.

To pull specific strings in a file, as noted you can use Out-GridView, as discussed here:

... or use the Select-String cmdlet on the file and remove or add data to that point.

Get-Content -Path 'D:\Temp\book1.txt'
# Results
<#
Site,Dept
Main,aaa,bbb,ccc
Branch1,ddd,eee,fff
Branch2,ggg,hhh,iii
#>
Select-String -Path 'D:\Temp\book1.txt' -Pattern '.*ggg.*'
# Results
<#
D:\Temp\book1.txt:4:Branch2,ggg,hhh,iii
#>

Note that you not only get the string pattern but the line it is on, thus, as suggested by @santiago-squarzon, no need to do this:

'...adding a "line number" before each line so the input can be an integer.'

Then you can get the line by that line number, without that effort. Yet, PowerShell offers several ways to do X or Y. So, here is an example combining Select-String and Get-Content to find and replace content in a file.

What is in the target file to be modified

Get-Content -Path 'D:\Temp\book1.txt'
# Results
<#
Site,Dept
Main,aaa,bbb,ccc
Branch1,ddd,eee,fff
Branch2,ggg,hhh,iii
#>

Provide a target string to take action on

Select-String -Path 'D:\Temp\book1.txt' -Pattern '.*ggg.*'
# Results
<#
D:\Temp\book1.txt:4:Branch2,ggg,hhh,iii
#>

Note, you not only get the string pattern but the line it is on, thus, as suggested by @santiago-squarzon, no need to do this: '...adding a "line number" before each line so the input can be an integer.'

Even if you chose not to do that, you can still use the Context switch/parameter to get that matched line. For example

Select-String -Path 'D:\Temp\book1.txt' -Pattern '.*ggg.*' -Context 0
# Results
<#
D:\Temp\book1.txt:4:Branch2,ggg,hhh,iii
#>

Now, If one needed to replace that line, with say null, then this. Note, I am using RegEx to remove the file path and line number text that is not needed.

$StringToReplace  = ((Select-String -Path 'D:\Temp\book1.txt' -Pattern '.*aaa.*' -Context 0) -replace '.*:\d+:')
$ReplacementString = 'null,null,null,null'
Set-Content -Path 'D:\Temp\book1.txt' -Value ((Get-Content -Path 'D:\Temp\book1.txt') -replace $StringToReplace, $ReplacementString)
Get-Content -Path 'D:\Temp\book1.txt'
# Results
<#
Site,Dept
Main,aaa,bbb,ccc
Branch1,ddd,eee,fff
null,null,null,null
#>

Again, graphically via the Out-GridView cmdlet is easier on your users. They simply filter, make a selection, and hit OK, then your code processing that selection similar to the above. Again, there are various ways to do this.

postanote
  • 15,138
  • 2
  • 14
  • 25
0

Instead of resorting to parts of a note in order to remove it, I would make use of the array index of the note. THat way, the user can enter a number and the function will delete the item with that index from the list.

Because a normal array you get with Get-Content has a fixed size, it is hard to remove one element from it. Below uses a List object to make that a lot easier.

Also, remember that Read-Host always returns a string (which might be numeric)..

Instead of all these if {..} statements, I'd recommend using switch for that.

Something like this:

$notePath = Join-Path -Path (Get-Location) -ChildPath "DONTREMOVEME.txt"
# use a Here-String to build the menu
$mainMenu = @"
What do you want to do?

1 - Add a note
2 - Read the notes
3 - Delete a note
Q - Quit

"@

# enter an endless loop
while ($true) {
    Clear-Host
    Write-Host $mainMenu
    $answer = Read-host "Write a number between 1 to 3 and press Enter"
    Clear-Host

    switch ($answer[0]) {   # [0] because we're only interested in the first character
        'Q'  {
            Write-host "Bye."
            return
        }
        '1' {
            do {  
                $msg = Read-host "Write the note and press Enter"
                Add-content -path $notePath -Value "- $msg"
                $answer = Read-host "Added, want to add one more? y/n"
            } 
            while ($answer -ne "n")
            break
        }
        '2' {
            Get-Content $notePath
            Write-Host
            Read-host "Press Enter to go back to the main menu"
            break
        }
        '3' {
            # deleting from a normal array is hard to do. This is why we make use of a List object for this
            $notes = [System.Collections.Generic.List[string]]@(Get-Content $notePath)
            # display the notes from the file and add a number in front
            for ($i = 1; $i -le $notes.Count; $i++) {
                '{0,3} {1}' -f $i, $notes[$i -1]   # {0,3} adds leading spaces to the number, in total 3 digits in this example
            }
            Write-Host
            $answer = Read-host "Press the number of the note to delete, then press Enter"
            # test if the user entered a number or not
            if ($answer -notmatch '\D') {
                $index = [int]$answer - 1
                if ($index -ge 0 -and $index -lt $notes.Count) {
                    # we have a valid index, remove that line and write the remaining notes to file
                    $notes.RemoveAt($index)
                    $notes | Set-Content -Path $notePath
                }
            }
            $notes.Clear()     
        }
    }
    Write-host "...back to the main menu"
    Start-Sleep -Seconds 2
}

P.S. $answer -notmatch '\D' is regex for if there is no character in the variable $answer that is something other than a digit. In other words, all characters must be digits

Theo
  • 57,719
  • 8
  • 24
  • 41