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 list landet) in die Datei geschrieben wird. Die History kannst du dann beim Programmstart in die list laden.

    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?


Anmelden zum Antworten