7

Given a history like this in Nushell, how do I delete specific entries; e.g. entries 6, 8, and 10?

nu > history
╭────┬────────────────────╮
│  # │      command       │
├────┼────────────────────┤
│  0 │ history --clear    │
│  1 │ php --version      │
│  2 │ composer --version │
│  3 │ node --version     │
│  4 │ npm --version      │
│  5 │ composer --version │
│  6 │ history            │
│  7 │ php --version      │
│  8 │ history            │
│  9 │ php --version      │
│ 10 │ history            │
│ 11 │ composer --version │
╰────┴────────────────────╯
Charles Roper
  • 20,125
  • 20
  • 71
  • 101

3 Answers3

11

nu's history command does not (yet) provide a functionality to delete items similar to bash's history -d. However, you can query where the history file is located using $nu.history-path, then use drop nth to delete the lines in question.

open $nu.history-path | lines | drop nth 6 8 10 | save -f $nu.history-path
pmf
  • 24,478
  • 2
  • 22
  • 31
11

Based on the code that I can read here and on the documentation here, it appears that an option like this is not currently available.

However, the code indicates that the history.txt file is located at ~/.config/nushell. Utilizing this information, it is possible to accomplish what you asked by using my script below:

nushell_history_manager.py

import os
import sys

def delete_lines(file_path, line_numbers):
    # open the file in read mode
    with open(file_path, 'r') as f:
        # read all the lines and store them in a list
        lines = f.readlines()
    # open the file in write mode
    with open(file_path, 'w') as f:
        for i, line in enumerate(lines):
            # check if the current line number is not in the list of line numbers to delete
            if i+1 not in line_numbers:
                # if it's not, write the line to the file
                f.write(line)

def print_table(file_path):
    # open the file in read mode
    with open(file_path, 'r') as f:
        # read all the lines and store them in a list
        lines = f.readlines()
    # print the table header
    print("╭──────┬───────────────────────────────╮")
    print("│  ##  │            command            │")
    print("├──────┼───────────────────────────────┤")
    for i, line in enumerate(lines):
        # print each line number and the corresponding command
        print(f"│ {i+1:3}  │ {line.strip():26}    │")
    # print the table footer
    print("╰──────┴───────────────────────────────╯")

if __name__ == '__main__':
    # set the file path to the history.txt file in the nushell config directory
    file_path = os.path.expanduser('~/.config/nushell/history.txt')
    # print the initial contents of the file in a table format
    print_table(file_path)
    # ask the user to enter line numbers to delete
    line_numbers_str = input("Enter line numbers to delete (separated by commas): ")
    # convert the entered line numbers to a list of integers
    line_numbers = list(map(int, line_numbers_str.split(',')))
    # delete the specified lines from the file
    delete_lines(file_path, line_numbers)
    # print the updated contents of the file in a table format
    print_table(file_path)

Usage:

python nushell_history_manager.py

Runtime:

╭──────┬────────────────────╮
│  ##  │      command       │
├──────┼────────────────────┤
│   1  │ history --clear    │
│   2  │ php --version      │
│   3  │ composer --version │
│   4  │ node --version     │
│   5  │ npm --version      │
│   6  │ composer --version │
│   7  │ history            │
│   8  │ php --version      │
│   9  │ history            │
│  10  │ php --version      │
│  11  │ history            │
│  12  │ composer --version │
╰──────┴────────────────────╯

Enter line numbers to delete (separated by commas): 7,9,11

╭──────┬────────────────────╮
│  ##  │      command       │
├──────┼────────────────────┤
│   1  │ history --clear    │
│   2  │ php --version      │
│   3  │ composer --version │
│   4  │ node --version     │
│   5  │ npm --version      │
│   6  │ composer --version │
│   7  │ php --version      │
│   8  │ php --version      │
│   9  │ composer --version │
╰──────┴────────────────────╯
Andreas Violaris
  • 2,465
  • 5
  • 13
  • 26
  • 2
    Very nice answer - thank you. I marked pmf's as the accepted one because it is idiomatic Nushell and doesn't require an external Python script. But I've upvoted yours too because it's great. Thanks again. – Charles Roper Jan 14 '23 at 02:24
  • I highly appreciate your feedback. You are most welcome! – Andreas Violaris Jan 14 '23 at 11:45
1

While @pmf response does EXACTLY what you ask, a more generic form is shown below. Here you don't even have to know the lines of the file that are duplicates:

export def dedupeLines [filepath: string = $"($nu.history-path)"] {
   open $filepath | lines | uniq | save --force $filepath  
}

In your specific case, filepath = $nu.history-path.

Executing the command below will accomplish your request

dedupeLines # since $nu.history-path is the default filepath for the command

Also for the future, Discords nushell has many questions and answers and the fellows there are extremely helpful and prompt in responding to queries. I had actually asked your question there before too.

I don't know if the python solution lines of code could be reduced, but it is interesting what nushell accomplishes in a single line when compared to the python solution.

st_clair_clarke
  • 5,453
  • 13
  • 49
  • 75
  • 1
    Thanks - great answer. Yes, it's very impressive how much you can do with a one-liner using Nushell, and even more impressive how it remains relatively readable, too. I'm already on the Discord and did ask this question there too. Unfortunately, the answers on Discord servers aren't on the open web and so can't be found via searching (unless you search in the Discord). This seemed a useful and generic enough question to ask here, too. :) – Charles Roper Jan 23 '23 at 16:12
  • 1
    st_clair_clarke it is certainly interesting and preferable. However, when conducting comparisons, it is crucial to ensure that the items being compared are of similar nature. In this case, it appears that you have compared a Python script that utilizes terminal UI to display and delete specific entries as chosen by the user, with a one-liner script that solely deletes duplicate entries. Therefore, the appropriate question to ask is whether the solution from Discord can be implemented in Python with a few lines, and the answer is yes. It can be done with a Python one-liner like the one below: – Andreas Violaris Jan 24 '23 at 20:08
  • 4
    `with open(os.path.expanduser('~/.config/nushell/history.txt'), "r+") as f: lines = f.readlines(); f.seek(0); f.truncate(); f.writelines(line for line in set(lines) if line.strip())` – Andreas Violaris Jan 24 '23 at 20:09