linux için c ++ shell
#include <cstring>
#include <map>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <filesystem>
#include <errno.h>
#include <bits/stdc++.h>
std::string USERDIR = getenv("HOME");
std::string ALIASFILE = USERDIR+"/shell/.alias";
std::vector<std::string> Split(std::string input, char delim);
void Execute(const char *command, char *arglist[]);
std::map<std::string, std::string> alias(std::string file);
bool BuiltInCom(const char *command, char *arglist[],int arglist_size);
char** conv(std::vector<std::string> source);
bool createAlias(std::string first, std::string sec);
std::string replaceAll(std::string data, std::map <std::string, std::string> dict);
int main() {
while (1) {
char path[100];
getcwd(path, 100);
char prompt[110] = "$[";
strcat(prompt, path);
strcat(prompt,"]: ");
std::cout << prompt;
// Takes input and splits it by space
std::string input;
getline(std::cin, input);
if(input == "") continue;
std::map<std::string, std::string> aliasDict = alias(ALIASFILE);
input = replaceAll(input, aliasDict);
std::vector<std::string> parsed_string = Split(input, ' ');
// Splits parsed_string into command and arglist
const char * com = parsed_string.front().c_str();
char ** arglist = conv(parsed_string);
// Checks if it is a built in command and if not, execute it
if(BuiltInCom(com, arglist, parsed_string.size()) == 0){
Execute(com, arglist);
}
delete[] arglist;
}
}
std::vector<std::string> Split(std::string input, char delim) {
std::vector<std::string> ret;
std::istringstream f(input);
std::string s;
while (getline(f, s, delim)) {
ret.push_back(s);
}
return ret;
}
void Execute(const char *command, char *arglist[]) {
pid_t pid;
//Creates a new proccess
if ((pid = fork()) < 0) {
std::cout << "Error: Cannot create new process" << std::endl;
exit(-1);
} else if (pid == 0) {
//Executes the command
if (execvp(command, arglist) < 0) {
std::cout << "Could not execute command" << std::endl;
exit(-1);
} else {
sleep(2);
}
}
//Waits for command to finish
if (waitpid(pid, NULL, 0) != pid) {
std::cout << "Error: waitpid()";
exit(-1);
}
}
bool BuiltInCom(const char *command, char ** arglist, int arglist_size){
if(strcmp(command, "quit") == 0){
delete[] arglist;
exit(0);
} else if(strcmp(command, "cd") == 0){
if(chdir(arglist[1]) < 0){
switch(errno){
case EACCES:
std::cout << "Search permission denied." << std::endl;
break;
case EFAULT:
std::cout << "Path points outside accesable adress space" << std::endl;
break;
case EIO:
std::cout << "IO error" << std::endl;
break;
case ELOOP:
std::cout << "Too many symbolic loops" << std::endl;
break;
case ENAMETOOLONG:
std::cout << "Path is too long" << std::endl;
break;
case ENOENT:
std::cout << "Path doesn't exist" << std::endl;
break;
case ENOTDIR:
std::cout << "Path isn't a dir" << std::endl;
break;
default:
std::cout << "Unknown error" << std::endl;
break;
}
return 1;
}
return 1;
} else if(strcmp(command, "alias") == 0){
if(arglist_size < 2){
std::cout << "[USAGE] Alias originalName:substituteName" << std::endl;
return 1;
}
std::string strArg(arglist[1]);
int numOfSpaces = std::count(strArg.begin(), strArg.end(), ':');
if(numOfSpaces){
std::vector<std::string> aliasPair = Split(strArg, ':');
createAlias(aliasPair.at(0), aliasPair.at(1));
return 1;
} else {
std::cout << "[USAGE] Alias originalName:substituteName" << std::endl;
return 1;
}
}
return 0;
}
char** conv(std::vector<std::string> source){
char ** dest = new char*[source.size() + 1];
for(int i = 0; i < source.size(); i++) dest[i] = (char *)source.at(i).c_str();
dest[source.size()] = NULL;
return dest;
}
std::map<std::string, std::string> alias(std::string file){
std::map<std::string, std::string> aliasPair;
std::string line;
std::ifstream aliasFile;
aliasFile.open(file);
if(aliasFile.is_open()){
while(getline(aliasFile, line)){
auto pair = Split(line, ':');
aliasPair.insert(std::make_pair(pair.at(0), pair.at(1)));
}
} else {
std::cout << "Error: Cannot open alias file\n";
}
return aliasPair;
}
std::string replaceAll(std::string data, std::map <std::string, std::string> dict){
for(std::pair <std::string, std::string> entry : dict){
size_t start_pos = data.find(entry.first);
while(start_pos != std::string::npos){
data.replace(start_pos, entry.first.length(),entry.second);
start_pos = data.find(entry.first, start_pos + entry.second.size());
}
}
return data;
}
bool createAlias(std::string first, std::string second){
std::ofstream aliasFile;
aliasFile.open(ALIASFILE, std::ios_base::app);
if(aliasFile.is_open()){
aliasFile << first << ":"<< second << std::endl;
return true;
} else return false;
}
Fedora Linux dağıtımında c ++ ile kodladığım bir kabuğum var. Kodun nasıl daha iyi hale getirileceğiyle ilgili genel iyileştirmeleri memnuniyetle isterim, ancak kodun okunabilirliği ile ilgili yorumları özellikle memnuniyetle isterim
Yanıtlar
Yalnızca c ++ standart kitaplık sınıflarını ve işlevlerini kullanarak bu kod için yapabileceğiniz çeşitli iyileştirmeler vardır.
1. kullanmayın #include <bits/stdc++.h>
Bu başlık dosyasının var olduğu garanti edilmez ve derleyiciye özgü bir dahili. Böyle kullanmak kodunuzu daha az taşınabilir hale getirecektir.
Yalnızca #includec ++ standart kitaplığından kullanmak istediğiniz sınıflar ve işlevler için sağlanan başlıklar.
Olası sonuçlar ve problemler hakkında daha fazla bilgiyi buradan okuyabilirsiniz: Neden #include
Ayrıca #include, onlardan hiçbir şey kullanmadığınız dosyaların üstbilgisini kullanmayın (örneğin #include <filesystem>).
2. Dize işlemleri için c kitaplığı işlevlerini kullanmayın
Örneğin, promptdeğişkeni oluşturmak için kodunuz, yalnızca aşağıdakiler std::stringyerine kullanılarak büyük ölçüde basitleştirilebilir char*:
char path[100];
getcwd(path,100);
std::string prompt = "$[" + std::string(path) + "]:";
Ayrıca yazabilirsin
if(command == "quit"){
sözde Kullanmak const std::string&için türü olarak commandparametresi.
3. char*Değişken dizilerini execxy()işlevlere iletmek için ayırmanıza gerek yoktur
Sadece işlevinizin std::vector<const char*>yerine bir tane oluşturduk conv():
void Execute(const std::string& command, const std::vector<std::string>& args) {
std::vector<const char*> cargs;
pid_t pid;
for(auto sarg : args) {
cargs.append(sarg.data());
}
cargs.append(nullptr);
//Creates a new proccess
if ((pid = fork()) < 0) {
std::cout << "Error: Cannot create new process" << std::endl;
exit(-1);
} else if (pid == 0) {
//Executes the command
if (execvp(command.data(), cargs.data()) < 0) {
std::cout << "Could not execute command" << std::endl;
exit(-1);
} else {
sleep(2);
}
}
//Waits for command to finish
if (waitpid(pid, NULL, 0) != pid) {
std::cout << "Error: waitpid()";
exit(-1);
}
}
Örn. Tarafından elde edilen ham veri işaretçileri kullandığınız böyle bir durumda std::string::data(), temel değişkenlerin yaşam sürelerinin kullanımları boyunca örneğin C kütüphanesi fonksiyonlarında sürdüğünden emin olun.
Genel bir kural olarak:
Bellek yönetimini kendiniz kullanarak newve deleteaçıkça yapmaktan kaçının . Bunun yerine bir c ++ standart kapsayıcı veya en azından akıllı işaretçiler kullanın .
4. boolDeğerler için açık bir karşılaştırmaya ihtiyacınız yok
Değişiklik
if(BuiltInCom(com, arglist, parsed_string.size()) == 0){
-e
if(!BuiltInCom(com, arglist, parsed_string.size())){
Ayrıca örtük dönüşümler ve değişmezler yerine falseve truekullanın .int 01
5. constMümkün olduğunda parametreler için referans olarak kullanın ve geçirin
Kullanım constparametreyi değiştirmenize gerek yoksa. Önemsiz olmayan türler için gereksiz kopyaların yapılmasını önlemek istiyorsanız,
referansla geçiş ( &) kullanın .
Nasıl olduğunu Execute()yukarıda verdiğim örnekte görebilirsiniz .
Aynısı örneğin
std::string replaceAll(std::string data, std::map <std::string, std::string> dict);
bu olmalı
std::string& replaceAll(std::string& data, const std::map <std::string, std::string>& dict);
Biçimlendirme.
Bu büyük bir metin duvarı. Okumayı daha kolay hale getirmek için şeyleri mantıksal bölümlere ayırmanız gerekir. Bunu okumayı kolaylaştırmak için bölüm arasına biraz dikey boşluk ekleyin.
Bir sürü #include öğeniz var. Onları sipariş etmek güzel. Mantıklı olduğu sürece sipariş vermenin herhangi bir yolunu seçebilir ve insanların bakmasını kolaylaştırabilirsiniz.
Ben en çok genel olana özel yaparım.
#include "HeaderFileForThisSource.h"
#include "HeaderFileForOtherClassesInThisProject"
...
#include <C++ Librries>
...
#include <C Librries>
...
#include <Standard C++ Header Files>
..
#include <C standard Libraries>
...
Diğerleri bunları alfabetik olarak listeler.
En iyisinin ne olduğundan emin değilim ama sipariş için biraz mantık güzel olurdu.
Bunu okumak gerçekten zor. Metin denizinde işlev adlarını göremiyorum.
std::vector<std::string> Split(std::string input, char delim);
void Execute(const char *command, char *arglist[]);
std::map<std::string, std::string> alias(std::string file);
bool BuiltInCom(const char *command, char *arglist[],int arglist_size);
char** conv(std::vector<std::string> source);
bool createAlias(std::string first, std::string sec);
std::string replaceAll(std::string data, std::map <std::string, std::string> dict);
Biraz bilinçli kullanım usingve biraz toparlama ile bunun kullanımını gerçekten kolaylaştırabilirsiniz.
using Store = std::vector<std::string>;
using Map = std::map<std::string, std::string>;
using CPPtr = char**;
Store Split(std::string input, char delim);
void Execute(const char *command, char *arglist[]);
Map alias(std::string file);
bool BuiltInCom(const char *command, char *arglist[],int arglist_size);
CPPtr conv(std::vector<std::string> source);
bool createAlias(std::string first, std::string sec);
std::string replaceAll(std::string data, std::map <std::string, std::string> dict);
Kod
Küresel "Değişkenler" kötü bir fikirdir.
std::string USERDIR = getenv("HOME");
std::string ALIASFILE = USERDIR+"/shell/.alias";
Bunu içinde ayarlayın main(). Daha sonra bunları parametre olarak geçirebilir veya bir nesneye ekleyebilirsiniz.
Global kapsamda statik değişmez duruma sahip olabilirsiniz. Bu, sabitler gibi şeyler içindir.
Okumayı kolaylaştırın.
while (1) {
Bu şu şekilde daha iyi olabilir:
while(true) {
Kullanıcının rastgele uzunlukta dizeler girebileceği sabit boyutlu arabellekleri kullanmayın. C ++ std::stringbu tür durumların üstesinden gelmek zorundadır .
char path[100];
getcwd(path, 100);
// Rather
std::string path = std::filesystem::current_path().string();
Kodunuzda sihirli sayılar kullanmayın:
char prompt[110] = "$[";
Neden 110? Sihirli sayıları adlandırılmış sabitlere koyun
// Near the top of the programe with all other constants.
// Then you can tune your program without having to search for the constants.
static std::size_t constepxr bufferSize = 110;
.....
char buffer[bufferSize];
Std :: string burada kullanılmalıdır
strcat(prompt, path);
strcat(prompt,"]: ");
Eski C string fonksiyonları güvenli değildir.