Wie Shell mit History erstellen?
-
Vielleicht kennt jemand die Bash, eine Shell aus der UNIX Welt. Alle eingegebenen Befehle werden in eine Datei abgespeichert, so kann man später wieder alte Befehle mithilfe der nach Oben Pfeiltaste diese Selektieren und ausführen.
Ich möchte auch so eine Funktion in meine eigenen Shell implementieren, doch es wird wahrscheinlich viel Arbeit

Ich habe folgenden Ansatz mit dem man Befehle einlesen kann, ganz simpel:
#include <windows.h> #include <iostream> #include <string> using namespace std; int main() { string Input; do { cout << "Eingabe:>"; getline(cin, Input); if(Input == "exit") return 0; //... else cout << "Befehl unbekannt" << endl << endl; } while(TRUE); return 0; }So, jetzt bräuchte ich euren Rat wie man bei der Eingabe gleichzeitig auf die Oben-Pfeiltaste warten kann und dann zum Test erstmal ein String ausgibt.
-
kernel64 schrieb:
So, jetzt bräuchte ich euren Rat wie man bei der Eingabe gleichzeitig auf die Oben-Pfeiltaste warten kann und dann zum Test erstmal ein String ausgibt.
getch()ist dein Freund...#include <string> #include <list> #include <iostream> #include <conio.h> using namespace std; void clear_line( string::size_type length, const string &prompt ) { cout.put( '\r' ); for( string::size_type i = 0; i < ( length + prompt.length( ) ); ++i ) { cout.put( ' ' ); } cout << '\r' << prompt << flush; } int main( ) { bool do_exit = false; list< string > history; const char prompt[ ] = ":\\"; do { cout << prompt; string input; bool history_mode = false; list< string >::iterator current_history_item = history.begin( ); int key; do { key = _getch( ); if( ( key == 0x08 ) && input.length( ) ) { // backspace clear_line( input.length( ), prompt ); input.erase( input.length( ) - 1, 1 ); cout << input << flush; } else if( key == 0x1b ) { // escape clear_line( input.length( ), prompt ); history_mode = false; input = ""; } else if( key == 0xe0 ) { // steuerzeichen switch( _getch( ) ) { case 0x48: // arrow key: up if( current_history_item != history.end( ) ) { if( !history_mode ) { history_mode = true; } else { ++current_history_item; } if( current_history_item != history.end( ) ) { clear_line( input.length( ), prompt ); input = *current_history_item; cout << input << flush; } } break; case 0x50: // arrow key: down if( history_mode && ( current_history_item != history.begin( ) ) ) { --current_history_item; clear_line( input.length( ), prompt ); input = *current_history_item; cout << input << flush; } break; } } else if( key != '\r' ) { input += key; cout.put( key ); } } while( key != '\r' ); // enter cout.put( '\n' ); if( input.length( ) ) { history.push_front( input ); } if( input == "exit" ) { do_exit = true; } } while( !do_exit ); }...ist einigermaßen quick 'n dirty, sollte aber das Prinzip klarstellen.
greetz, Swordfish
-
Ohhh vielen Dank, da hat sich jemand Mühe gemacht

Ich habe nun die eingegebenen Befehle in eine Datei gespeichert:
if( input.length( ) ) { saveToHistory(input); history.push_front( input ); }Hier die Funktion zum speichern:
void saveToHistory(string command) { ofstream datei("history.txt", ios::app); if( !datei ) throw "Fehler beim Öffnen!"; datei << command << endl; datei.close(); }Jetzt habe ich eine Funktion zum auslesen der strings:
string readFromHistory() { ifstream datei("history.txt"); string buffer; if( !datei ) throw "Fehler beim Öffnen!"; getline(datei, buffer,'\n'); datei.close(); return buffer; }Doch wie muss ich diese in der Switch Anweisung ber nach OBEN und UNTEN Pfeiltaste verwenden, ein Problem ist hierbei das immer nur der erste String ausgelesen wird, wahrscheinlich muss man den Code in der Switch Anweisung einbauen?!?!?
-
Du brauchst die History nicht ständig aus der Datei lesen, sondern musst nur sicherstellen, dass jede Eingabe (die auch in der
listlandet) in die Datei geschrieben wird. Die History kannst du dann beim Programmstart in dielistladen.greetz, Swordfish
-
Hab nun folgendes:
list<string> readHistory() { list< string > history; ifstream datei("history.txt"); string buffer; if( !datei ) throw "Fehler beim Öffnen!"; while (getline(datei, buffer, '\n')) { history.push_back(buffer); } datei.close(); return history; } int main( ) { bool do_exit = false; const char prompt[ ] = ":\\"; list< string > history; history = readHistory(); do { cout << prompt; string input; bool history_mode = false; list< string >::iterator current_history_item = history.begin( ); int key; do { key = _getch( ); //Backspace if( ( key == 0x08 ) && input.length( ) ) { clear_line( input.length( ), prompt ); input.erase( input.length( ) - 1, 1 ); cout << input << flush; } //TAB else if( key == 0x09) { cout << "TAB"; } //Escape else if( key == 0x1b ) { clear_line( input.length( ), prompt ); history_mode = false; input = ""; } //Steuerzeichen else if( key == 0xe0 ) { switch( _getch( ) ) { case 0x48: // arrow key: up if( current_history_item != history.end( ) ) { if( !history_mode ) { history_mode = true; } else { ++current_history_item; } if( current_history_item != history.end( ) ) { clear_line( input.length( ), prompt ); input = *current_history_item; cout << input << flush; } } break; case 0x50: // arrow key: down if( history_mode && ( current_history_item != history.begin( ) ) ) { --current_history_item; clear_line( input.length( ), prompt ); input = *current_history_item; cout << input << flush; } break; } } else if( key != '\r' ) { input += key; cout.put( key ); } }while( key != '\r' ); // enter cout.put( '\n' ); if( input.length( ) ) { history.push_front( input ); //Speichert Eingabe in History saveToHistory(input); } if( input == "exit" ) { do_exit = true; } } while( !do_exit ); }Die Funktion readHistory liest die Datei aus und speichert es dann in eine Liste. Jetzt kann man die Liste von unten nach oben auslesen:
while (history.size() > 0) { string str; str = history.back(); cout << "Inhalt: " << str << endl; history.pop_back(); }Wie baut man diesen Teil in den Code ein, in der Switch Anweisung oder an welcher Stelle???
-
Hallo, der Code mit der History Funktion funktioniert einwandfrei, muss mich wirklich bei dir bedanken

So da ich schon immer die TAB-Vervollständigung auch implementieren wollte und es bisher nicht geschafft habe ich dein Code modifiziert und bin erstaunlich weit gekommen

int main( ) { string HistoryFile = "history.txt"; bool do_exit = false; const char prompt[ ] = ":\\>"; //Liste für History Befehle list< string > history; //Liste für Dateien/Verzeichniss list< string > listDir; if(checkFile(HistoryFile.c_str())) { history = readHistory(HistoryFile); } else cout << "History nicht gefunden" << endl; //Hole Dateien/Verzeichnisse listDir = getDirectory(); do { cout << prompt; string input; bool history_mode = false; //Liste fängt am Anfang an list< string >::iterator current_file = listDir.begin(); list< string >::iterator current_history_item = history.begin( ); int key; do { key = _getch( ); //Backspace if( ( key == 0x08 ) && input.length( ) ) { clear_line( input.length( ), prompt ); input.erase( input.length( ) - 1, 1 ); cout << input << flush; } //TAB-Vervollständigung else if( key == 0x09) { if( current_file != listDir.end() ) { ++current_file; if( current_file != listDir.end( ) ) { clear_line( input.length( ), prompt ); input = *current_file; cout << input << flush; } else current_file = listDir.begin(); } } //Escape else if( key == 0x1b ) { clear_line( input.length( ), prompt ); history_mode = false; input = ""; } //Steuerzeichen else if( key == 0xe0 ) { switch( _getch( ) ) { case 0x48: // arrow key: up if( current_history_item != history.end( ) ) { if( !history_mode ) { history_mode = true; } else { ++current_history_item; } if( current_history_item != history.end( ) ) { clear_line( input.length( ), prompt ); input = *current_history_item; cout << input << flush; } } break; case 0x50: // arrow key: down if( history_mode && ( current_history_item != history.begin( ) ) ) { --current_history_item; clear_line( input.length( ), prompt ); input = *current_history_item; cout << input << flush; } break; } } else if( key != '\r' ) { input += key; cout.put( key ); } }while( key != '\r' ); // enter cout.put( '\n' ); if( input.length( ) ) { history.push_front( input ); //Speichert Eingabe in History saveToHistory(input); } if( input == "exit" ) { do_exit = true; } } while( !do_exit ); }Wenn ich nun TAB drücke werden nacheinander die Dateien und Verzeichnisse aufgelistet, doch sobald ich den Befehl cd eingabe und dann TAB drücke wird dieser überschrieben, da die clear_line() Funktion aufgerufen wird, wie kann man an der aktuelle Cursor position die Verzeichnisse ausgeben lassen?
-
So langsam komme ich voran

Hier die verbesserte Version:int main( ) { string HistoryFile = "history.txt"; bool do_exit = false; const char prompt[ ] = ":\\>"; //Liste für History Befehle list< string > history; //Liste für Dateien/Verzeichniss list< string > listDir; if(checkFile(HistoryFile.c_str())) { history = readHistory(HistoryFile); } //Hole Dateien/Verzeichnisse listDir = getDirectory(); HANDLE std_output = GetStdHandle( STD_OUTPUT_HANDLE ); CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info; do { cout << prompt; string input; string getTabOutput; bool history_mode = false; bool TAB_MODE = false; list< string >::iterator current_file = listDir.begin(); list< string >::iterator current_history_item = history.begin( ); int key; do { key = _getch( ); //Backspace if( ( key == 0x08 ) && input.length( ) ) { clear_line( input.length( ), prompt ); input.erase( input.length( ) - 1, 1 ); cout << input << flush; } //TAB-Vervollständigung else if( key == 0x09) { //Speichert Ausgabe in History TAB_MODE = true; if( current_file != listDir.end() ) { ++current_file; if( current_file != listDir.end( ) ) { string FileObject = *current_file; getTabOutput = clear_TabInput(input, prompt, FileObject); cout << getTabOutput << flush; } else current_file = listDir.begin(); } } else if( key == 0x0E && key == 0x09) { cout << "SHIFT" << endl; } //Escape else if( key == 0x1b ) { clear_line( input.length( ), prompt ); history_mode = false; input = ""; } //Steuerzeichen else if( key == 0xe0 ) { switch( _getch( ) ) { int x, y; case 0x48: // Pfeiltaste nach Oben if( current_history_item != history.end( ) ) { if( !history_mode ) { history_mode = true; } else { ++current_history_item; } if( current_history_item != history.end( ) ) { clear_line( input.length( ), prompt ); input = *current_history_item; cout << input << flush; } } break; case 0x50: // Pfeiltaste nach Unten if( history_mode && ( current_history_item != history.begin( ) ) ) { --current_history_item; clear_line( input.length( ), prompt ); input = *current_history_item; cout << input << flush; } break; case 0x4B: // Pfeiltaste nach Links GetConsoleScreenBufferInfo( std_output, &console_screen_buffer_info ); x = console_screen_buffer_info.dwCursorPosition.X; y = console_screen_buffer_info.dwCursorPosition.Y; gotoxy(x-1, y); break; case 0x4D: // Pfeiltaste nach Rechts GetConsoleScreenBufferInfo( std_output, &console_screen_buffer_info ); x = console_screen_buffer_info.dwCursorPosition.X; y = console_screen_buffer_info.dwCursorPosition.Y; gotoxy(x+1, y); break; } } else if( key != '\r' ) { input += key; cout.put( key ); } else if( key == '\r') { if(TAB_MODE) saveToHistory(getTabOutput); else saveToHistory(input); } }while( key != '\r' ); // enter cout.put( '\n' ); if( input.length( ) ) { if(TAB_MODE) history.push_front( getTabOutput); else history.push_front( input ); } if( input == "exit" ) { do_exit = true; } } while( !do_exit ); }Jetzt habe ich noch die LINKE & RECHTE PFEILTASTE eingebaut um in dem eingegebenem string zu navigieren, mithilfe von gotoxy() kann ich nun an die gewünschte Position gelangen. Wenn ich nun die Leertaste drücke wird der Input gelöscht also das jeweilige Zeichen, z.B. ich als Input = "hallo test**"
Jetzt möchte ich mit dem Cursor vorm "test" = "**test" wenn ich hier Leertaste drücke steht folgendes: " _est" es wird alles verschlungen, wie kann man das verhindern?